Source packages and functions

source(here::here("code", "scripts", "20200318_notebook_source.R"))
Loading required package: ape
Loading required package: adegenet
Loading required package: ade4

   /// adegenet 2.1.3 is loaded ////////////

   > overview: '?adegenet'
   > tutorials/doc/questions: 'adegenetWeb()' 
   > bug reports/feature requests: adegenetIssues()



Attaching package: ‘pegas’

The following object is masked from ‘package:ade4’:

    amova

The following object is masked from ‘package:ape’:

    mst

Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ─────────────────────────────────────────────────────── tidyverse 1.3.0 ──
✓ ggplot2 3.3.1     ✓ purrr   0.3.4
✓ tibble  3.0.1     ✓ dplyr   1.0.0
✓ tidyr   1.1.0     ✓ stringr 1.4.0
✓ readr   1.3.1     ✓ forcats 0.5.0
── Conflicts ────────────────────────────────────────────────────────── tidyverse_conflicts() ──
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()

20200130

Data

• 1KG final release VCFs: ftp://ftp.1000genomes.ebi.ac.uk/vol1/ftp/release/20130502/ • Educational attainment paper: https://www.nature.com/articles/s41588-018-0147-3 • Lead loci from that paper: racist_hypothesis/data/20200130_iq_gwas_topf.csv

Papers:

• Berg J. J., Coop G., 2014 ‘A population genetic signal of polygenic adaptation’. PLoS Genetics 10: e1004412 • Also their 2017 paper https://www.biorxiv.org/content/10.1101/167551v4 • Spiedel et al on a tool for inferring genealogy (and hence changes in allele frequencies, hence selection): https://www.nature.com/articles/s41588-019-0484-x

Cluster structure

Home: /hps/research1/birney/users/ian/rac_hyp

mkcd vcfs

# download VCF and index
wget ftp://ftp.1000genomes.ebi.ac.uk/vol1/ftp/release/20130502/ALL.wgs.phase3_shapeit2_mvncall_integrated_v5b.20130502.sites.vcf.gz
wget ftp://ftp.1000genomes.ebi.ac.uk/vol1/ftp/release/20130502/ALL.wgs.phase3_shapeit2_mvncall_integrated_v5b.20130502.sites.vcf.gz.tbi

Update GATK

cd /nfs/software/birney
wget https://github.com/broadinstitute/gatk/releases/download/4.1.4.1/gatk-4.1.4.1.zip
unzip gatk-4.1.4.1.zip

# amend aliases in ~/.bashrc and ~/.bash_profile
export PATH=$PATH:/nfs/software/birney/gatk-4.1.4.1/

Set up reference

mkcd refs
# download FASTA
wget ftp://ftp.ensembl.org/pub/release-99/fasta/homo_sapiens/dna/Homo_sapiens.GRCh38.dna.toplevel.fa.gz
# create dictionary follwoing guiadance here: <https://gatkforums.broadinstitute.org/gatk/discussion/1601/how-can-i-prepare-a-fasta-file-to-use-as-reference>
java -jar /nfs/software/birney/picard-2.9.0/picard.jar CreateSequenceDictionary \
  R=refs/Homo_sapiens.GRCh38.dna.toplevel.fa.gz \
  O=refs/Homo_sapiens.GRCh38.dna.toplevel.dict
# create fasta index file
/nfs/software/birney/samtools-1.9/samtools faidx refs/Homo_sapiens.GRCh38.dna.toplevel.fa.gz
# [E::fai_build3_core] Cannot index files compressed with gzip, please use bgzip
## unzip 
gunzip refs/Homo_sapiens.GRCh38.dna.toplevel.fa.gz
## create dictionary again
rm refs/Homo_sapiens.GRCh38.dna.toplevel.dict
java -jar /nfs/software/birney/picard-2.9.0/picard.jar CreateSequenceDictionary \
  R=refs/Homo_sapiens.GRCh38.dna.toplevel.fa \
  O=refs/Homo_sapiens.GRCh38.dna.toplevel.dict
# create gast index file
/nfs/software/birney/samtools-1.9/samtools faidx refs/Homo_sapiens.GRCh38.dna.toplevel.fa

Create list of SNPs from top hits table: racist_hypothesis/data/20200204_snps.list

cut racist_hypothesis/data/20200204_iq_gwas_topf -f5 -d"," | sed 's/"//g' | tail -n+2 > racist_hypothesis/data/20200204_snps.list 

Pull out SNPs from VCF based on loci

gatk SelectVariants \
  -R refs/Homo_sapiens.GRCh38.dna.toplevel.fa \
  -V vcfs/ALL.wgs.phase3_shapeit2_mvncall_integrated_v5b.20130502.sites.vcf.gz \
  --keep-ids racist_hypothesis/data/20200204_snps.list \
  -O vcfs/ALL.hits.vcf.gz
# Error initializing feature reader for path vcfs/ALL.wgs.phase3_shapeit2_mvncall_integrated_v5b.20130502.sites.vcf.gz
# Caused by: htsjdk.tribble.TribbleException$MalformedFeatureFile: Unable to parse header with error: Invalid GZIP header, for input source: vcfs/ALL.wgs.phase3_shapeit2_mvncall_integrated_v5b.20130502.sites.vcf.gz  

This VCF doesn’t have the per-sample calls, but just the allele frequencies for each continental population.

Have to download all the VCFs on the page, then put them together.

Download

wget -r -p -k --no-parent -cut-dirs=5 ftp://ftp.1000genomes.ebi.ac.uk/vol1/ftp/release/20130502/

Put list of files into list

find vcfs/ftp.1000genomes.ebi.ac.uk/ALL.chr*.vcf.gz > racist_hypothesis/data/20200205_vcfs.list

Merge VCFs

java -jar /nfs/software/birney/picard-2.9.0/picard.jar MergeVcfs \
  I=racist_hypothesis/data/20200205_vcfs.list \
  O=vcfs/1gk_all.vcf.gz
# Exception in thread "main" java.lang.IllegalArgumentException: The contig entries in input file /hps/research1/birney/users/ian/rac_hyp/vcfs/ftp.1000genomes.ebi.ac.uk/ALL.chrMT.phase3_callmom-v0_4.20130502.genotypes.vcf.gz are not compatible with the others.

# So remove that one from list above
sed -i '/MT/d' racist_hypothesis/data/20200205_vcfs.list

# run MergeVCFs again
java -jar /nfs/software/birney/picard-2.9.0/picard.jar MergeVcfs \
  I=racist_hypothesis/data/20200205_vcfs.list \
  O=vcfs/1gk_all.vcf.gz
  
# Exception in thread "main" java.lang.IllegalArgumentException: The contig entries in input file /hps/research1/birney/users/ian/rac_hyp/vcfs/ftp.1000genomes.ebi.ac.uk/ALL.chrY.phase3_integrated_v2a.20130502.genotypes.vcf.gz are not compatible with the others.
sed -i '/chrY/d' racist_hypothesis/data/20200205_vcfs.list

# run MergeVCFs again
java -jar /nfs/software/birney/picard-2.9.0/picard.jar MergeVcfs \
  I=racist_hypothesis/data/20200205_vcfs.list \
  O=vcfs/1gk_all.vcf.gz
# WORKS

Get reference used for this callset

# Find out which reference is used by the callset
bcftools view vcfs/1gk_all.vcf.gz | less
# ftp://ftp.1000genomes.ebi.ac.uk//vol1/ftp/technical/reference/phase2_reference_assembly_sequence/hs37d5.fa.gz

# download that reference
cd refs
wget ftp://ftp.1000genomes.ebi.ac.uk//vol1/ftp/technical/reference/phase2_reference_assembly_sequence/hs37d5.fa.gz
# create index
/nfs/software/birney/samtools-1.9/samtools faidx refs/hs37d5.fa.gz
# create dictionary
java -jar /nfs/software/birney/picard-2.9.0/picard.jar CreateSequenceDictionary \
  R=refs/hs37d5.fa.gz \
  O=refs/hs37d5.dict

Pull out SNPs

gatk SelectVariants \
  -R refs/hs37d5.fa.gz \
  -V vcfs/1gk_all.vcf.gz \
  --keep-ids racist_hypothesis/data/20200204_snps.list \
  -O vcfs/snp_hits.vcf.gz
# SUCCESS

Copy to repo

cp vcfs/snp_hits.vcf.gz racist_hypothesis/data/20200303_snp_hits.vcf.gz
cp vcfs/snp_hits.vcf.gz.tbi racist_hypothesis/data/20200303_snp_hits.vcf.gz.tbi

Import into R

snp_hits <- pegas::read.vcf(here::here("data", "20200303_snp_hits.vcf.gz"))

Import sample info

From here: http://ftp.1000genomes.ebi.ac.uk/vol1/ftp/technical/working/20130606_sample_info/20130606_sample_info.xlsx (link embedded in this page: <www.internationalgenome.org/data>)

meta <- read_xlsx(here::here("data", "20130606_sample_info.xlsx"), sheet = "Sample Info") %>% dplyr::select(Sample, Population, Gender)
Expecting logical in N1378 / R1378C14: got 'HG02624'Expecting logical in N1387 / R1387C14: got 'HG02624, HG02610, HG02666'Expecting logical in N1417 / R1417C14: got 'HG02666, HG02624'Expecting logical in N1426 / R1426C14: got 'HG02682'Expecting logical in N1427 / R1427C14: got 'HG02681'Expecting logical in N1432 / R1432C14: got 'HG03705'Expecting logical in N1442 / R1442C14: got 'HG02700'Expecting logical in N1443 / R1443C14: got 'HG02699'Expecting logical in N1642 / R1642C14: got 'HG03097'Expecting logical in N1658 / R1658C14: got 'HG03073'Expecting logical in N1734 / R1734C14: got 'HG03229'Expecting logical in N1735 / R1735C14: got 'HG03228'Expecting logical in N1758 / R1758C14: got 'HG03271'Expecting logical in N1761 / R1761C14: got 'HG03268'Expecting logical in N1774 / R1774C14: got 'HG03372'Expecting logical in N1791 / R1791C14: got 'HG03352'Expecting logical in N1795 / R1795C14: got 'HG03366, HG03343'Expecting logical in N1801 / R1801C14: got 'HG03352'Expecting logical in N1807 / R1807C14: got 'HG03301'Expecting logical in N1812 / R1812C14: got 'HG03383'Expecting logical in N1815 / R1815C14: got 'HG03383'Expecting logical in N1832 / R1832C14: got 'HG03457'Expecting logical in L1838 / R1838C12: got 'HG03471'Expecting logical in N1848 / R1848C14: got 'HG03457'Expecting logical in N1849 / R1849C14: got 'HG03457'Expecting logical in N1853 / R1853C14: got 'HG03457'Expecting logical in N1857 / R1857C14: got 'HG03484'Expecting logical in N1862 / R1862C14: got 'HG03480, HG03484'Expecting logical in L1863 / R1863C12: got 'HG03438'Expecting logical in N1871 / R1871C14: got 'HG03469, HG03484'Expecting logical in N1872 / R1872C14: got 'HG03478, HG03469, HG03480'Expecting logical in N1899 / R1899C14: got 'HG03566'Expecting logical in N1902 / R1902C14: got 'HG03574'Expecting logical in N1909 / R1909C14: got 'HG03547'Expecting logical in N1914 / R1914C14: got 'HG03556'Expecting logical in N2007 / R2007C14: got 'HG02687'Expecting logical in N2365 / R2365C14: got 'NA07022, NA07056, first cous once removed'Expecting logical in N2372 / R2372C14: got 'NA06993 (1st cous once removed)'Expecting logical in N2381 / R2381C14: got 'NA06993 (1st cous once removed)'Expecting logical in N2457 / R2457C14: got 'NA12264 (1st cous once removed)'Expecting logical in N2463 / R2463C14: got 'NA12155 (1st cous once removed)'Expecting logical in H2561 / R2561C8: got 'NA18510'Expecting logical in H2562 / R2562C8: got 'NA18509'Expecting logical in H2711 / R2711C8: got 'NA19252'Expecting logical in L2716 / R2716C12: got 'NA19240'Expecting logical in N2818 / R2818C14: got 'NA19022, NA19045'Expecting logical in N2927 / R2927C14: got 'NA19178 (1st cous once removed)'Expecting logical in H2957 / R2957C8: got 'NA18908'Expecting logical in N2981 / R2981C14: got 'NA19334'Expecting logical in N2983 / R2983C14: got 'NA19331'Expecting logical in L3002 / R3002C12: got 'NA19382'Expecting logical in L3004 / R3004C12: got 'NA19380'Expecting logical in N3006 / R3006C14: got 'NA19025'Expecting logical in N3022 / R3022C14: got 'NA19037, NA19039'Expecting logical in H3205 / R3205C8: got 'NA19714'Expecting logical in L3215 / R3215C12: got 'NA20284,NA20285'Expecting logical in H3216 / R3216C8: got 'NA20284'Expecting logical in H3218 / R3218C8: got 'NA20281'Expecting logical in L3218 / R3218C12: got 'NA20279'Expecting logical in L3219 / R3219C12: got 'NA20279'Expecting logical in H3230 / R3230C8: got 'NA20300'Expecting logical in L3259 / R3259C12: got 'NA20363'Expecting logical in L3272 / R3272C12: got 'NA20347'Expecting logical in N3477 / R3477C14: got 'NA21134'Expecting logical in N3495 / R3495C14: got 'NA21114'

Attach population column to snp_hits

# create population vector
populations <- unlist(lapply(rownames(snp_hits), function(sample){
  meta$Population[meta$Sample == sample]
}))

# add to snp_hits
snp_hits$population <- populations
# reorder
snp_hits <- snp_hits %>% dplyr::select(population, everything())
# split by population
snp_hits_split <- split(snp_hits, f = snp_hits$population)
# remove population columns
snp_hits_split <- lapply(snp_hits_split, function(x){
  x$population <- NULL
  return(x)
})

# get allele counts
allele_counts <- lapply(snp_hits_split, function(population){
  summary(population)
})

Import list of SNPs with alleles

snp_al_nos <- read.delim("~/Documents/Repositories/racist_hypothesis/data/20200303_top_snps_with_alleles.txt", as.is = T)

Create data frames with allele frequences

# TEST
test <- allele_counts[["ACB"]][["rs79582714"]]
test2 <- data.frame("allele" = names(test$allele),
                    "count" = test$allele)
test2$al_freq <- test2$count / sum(test2$count)
z_scores <- data.frame("allele" = c(snp_al_nos$Allele1[snp_al_nos$SNP == "rs79582714"],
                                    snp_al_nos$Allele2[snp_al_nos$SNP == "rs79582714"]),
                       "z_score" = c(0, snp_al_nos$Z.Score[snp_al_nos$SNP == "rs79582714"]))
test3 <- dplyr::left_join(test2, z_scores, by = "allele")

# Create function
get_alfreq_table <- function(population, snp_effects_df){
  counter <- 0
  population <- lapply(population, function(snp_summary){
    # set counter and extract SNP ID
    counter <<- counter + 1
    snp_id <- names(population)[counter]
    # create final DF
    final_df <- data.frame("allele" = names(snp_summary$allele),
                           "count" = snp_summary$allele,
                           stringsAsFactors = F)
    # get allele frequency
    final_df$al_freq <- final_df$count / sum(final_df$count)
    # pull out z-scores and p-values from snp_effects_df
    z_scores <- data.frame("allele" = c(snp_effects_df$Allele1[snp_effects_df$SNP == snp_id],
                                        snp_effects_df$Allele2[snp_effects_df$SNP == snp_id]),
                           "z_score" = c(0, snp_effects_df$Z.Score[snp_effects_df$SNP == snp_id]),
                           "p_value" = c(0, snp_effects_df$P[snp_effects_df$SNP == snp_id]),
                           stringsAsFactors = F)
    # join DFs. Note that it only joins the two alleles in the snp_effects_df
    final_df <- dplyr::left_join(z_scores, final_df, by = "allele")
    return(final_df)
  })
  return(population)
}

# Run over list
alcnt_df_lst <- lapply(allele_counts, function(x){
  out <- get_alfreq_table(x, snp_al_nos)
  final <- dplyr::bind_rows(out, .id = "rsid")
  return(final)
})

# create final DF
alcnt_df <- dplyr::bind_rows(alcnt_df_lst, .id = "population")

# filter only for alleles with an efffect
alcnt_df_filt <- alcnt_df[alcnt_df$z_score != 0, ]

# set new rownames
rownames(alcnt_df_filt) <- seq(1:nrow(alcnt_df_filt))

Plot

Separate columns to plot on either axis

Plot

YRI v CEU

ggplot(data = dplyr::filter(plot_df, z_score <= 0), aes(al_freq_YRI, al_freq_CEU, colour = z_score)) +
  geom_point() +
  scale_color_viridis_c() +
  coord_fixed() +
  geom_smooth(se = F) +
  geom_abline(intercept = 0, slope = 1) +
  ggtitle("IQ-negative alleles")


ggplot(data = dplyr::filter(plot_df, z_score >= 0), aes(al_freq_YRI, al_freq_CEU, colour = z_score)) +
  geom_point() +
  scale_color_viridis_c() +
  coord_fixed() +
  geom_smooth(se = F) +
  geom_abline(intercept = 0, slope = 1) +
  ggtitle("IQ-positive alleles")

YRI v CHS

ggplot(data = dplyr::filter(plot_df, z_score <= 0), aes(al_freq_YRI, al_freq_CHS, colour = z_score)) +
  geom_point() +
  scale_color_viridis_c() +
  coord_fixed() +
  geom_smooth(se = F) +
  geom_abline(intercept = 0, slope = 1) +
  ggtitle("IQ-negative alleles")


ggplot(data = dplyr::filter(plot_df, z_score >= 0), aes(al_freq_YRI, al_freq_CHS, colour = z_score)) +
  geom_point() +
  scale_color_viridis_c() +
  coord_fixed() +
  geom_smooth(se = F) +
  geom_abline(intercept = 0, slope = 1) +
  ggtitle("IQ-positive alleles")

CEU v CHS

ggplot(data = dplyr::filter(plot_df, z_score <= 0), aes(al_freq_CEU, al_freq_CHS, colour = z_score)) +
  geom_point() +
  scale_color_viridis_c() +
  coord_fixed() +
  geom_smooth(se = F) +
  geom_abline(intercept = 0, slope = 1) +
  ggtitle("IQ-negative alleles")


ggplot(data = dplyr::filter(plot_df, z_score >= 0), aes(al_freq_CEU, al_freq_CHS, colour = z_score)) +
  geom_point() +
  scale_color_viridis_c() +
  coord_fixed() +
  geom_smooth(se = F) +
  geom_abline(intercept = 0, slope = 1) +
  ggtitle("IQ-positive alleles")

Run Fst

Identify SNPs with Fst above 0.2

test$SNP[test$Fst > 0.2]
 [1] "rs1408579"  "rs56151722" "rs7534501"  "rs4294650"  "rs1343700"  "rs2420551"  "rs11126396"
 [8] "rs11168951" "rs28612284" "rs6124077"  "rs6706409" 

Plot above with labels for these SNPs

ggplot(data = dplyr::filter(plot_df, z_score <= 0), aes(al_freq_YRI, al_freq_CEU, colour = z_score, label = rsid)) +
  geom_point() +
  geom_label_repel(aes(label = ifelse(rsid %in% high_fst_snps, rsid, ''),point = 0.5)) +
  #geom_text(aes(label = ifelse(rsid %in% high_fst_snps, rsid, '')), hjust = 0, vjust = 0, colour = "black") +
  scale_color_viridis_c() +
  coord_fixed() +
  geom_smooth(se = F) +
  geom_abline(intercept = 0, slope = 1) +
  ggtitle("IQ-negative alleles")
Ignoring unknown aesthetics: point

Run again on SNPs from Lee et al (2019) Gene discovery and polygenic prediction from a genome-wide association study of educational attainment in 1.1 million individuals

Pull out SNPs from paper

# extract from excel doc
snps_eduyrs <- read_xlsx("~/Documents/Repositories/racist_hypothesis/data/20180723_Lee-et-al_supp-tables.xlsx", sheet = "2. EduYears Lead SNPs", skip = 1, n_max = 1271)
# write table of SNPs
write.table(snps_eduyrs[["SNP"]], "~/Documents/Repositories/racist_hypothesis/data/20200316_snps_eduyears.list", quote = F, row.names = F, col.names = F)

Select those SNPs from the VCF

gatk SelectVariants \
  -R refs/hs37d5.fa.gz \
  -V vcfs/1gk_all.vcf.gz \
  --keep-ids racist_hypothesis/data/20200316_snps_eduyears.list \
  -O vcfs/snphits_eduyrs.vcf.gz

Get more SNPs for height

From the GWAS catalog here: https://www.ebi.ac.uk/gwas/efotraits/EFO_0004339.

Don’t include child trait data. Leaves 4907 associations.

Saved here: ~/Documents/Repositories/racist_hypothesis/data/20200304_raw_associations_height.csv

# pull out rsIDs
cat data/20200304_raw_associations_height.csv | cut -f1 -d'-' > tmp_rsid.txt 
# pull out lines with risk alleles only 
grep "<b>\w</b>" data/20200304_raw_associations_height.csv > data/20200316_raw_associations_height_edited.csv
# pull out rsIDs and paste into edited doc
cut -f1 -d'-' data/20200316_raw_associations_height_edited.csv
# note: split "Variant and risk allele" column into two manually

20200316

Obtain data

A bit messy. Try getting the height SNPs from this paper instead:

Yengo et al. (2018) Meta-analysis of genome-wide association studies for height and body mass index in approximately 700000 individuals of European ancestry

Data downloaded from here: https://portals.broadinstitute.org/collaboration/giant/index.php/GIANT_consortium_data_files More specifically: https://portals.broadinstitute.org/collaboration/giant/images/e/e2/Meta-analysis_Locke_et_al%2BUKBiobank_2018_top_941_from_COJO_analysis_UPDATED.txt.gz

cd racist_hypothesis/data
# download
wget https://portals.broadinstitute.org/collaboration/giant/images/e/e2/Meta-analysis_Locke_et_al%2BUKBiobank_2018_top_941_from_COJO_analysis_UPDATED.txt.gz
# unzip
gunzip Meta-analysis_Locke_et_al%2BUKBiobank_2018_top_941_from_COJO_analysis_UPDATED.txt.gz
# rename
mv Meta-analysis_Wood_et_al+UKBiobank_2018_top_3290_from_COJO_analysis.txt 20181015_Yengo-et-al_snps_height.txt

Pull out list of SNPs

cut -f1 20181015_Yengo-et-al_snps_height.txt | tail -n+2 > 20200318_snps_height.list

Extract calls for those SNPs from VCF

gatk SelectVariants \
  -R refs/hs37d5.fa.gz \
  -V vcfs/1gk_all.vcf.gz \
  --keep-ids racist_hypothesis/data/20200318_snps_height.list \
  -O vcfs/snphits_height.vcf.gz

20200318

Get SNPs for IBD.

Huang et al. (2017) Fine-mapping inflammatory bowel disease loci to single-variant resolution

Data downloaded here: https://www.nature.com/articles/nature22969#Sec29. More specifically, Supplementary Table 1: https://static-content.springer.com/esm/art%3A10.1038%2Fnature22969/MediaObjects/41586_2017_BFnature22969_MOESM2_ESM.xlsx Saved here: data/20170628_Huang-et-al_supp-table-1.xlsx

Pull out SNPs from paper

# extract from excel doc
snps_ibd <- read_xlsx(here::here("data", "20170628_Huang-et-al_supp-table-1.xlsx"),
                      sheet = "list of variants")
New names:
* trait -> trait...7
* trait -> trait...8

Get VCFs

gatk SelectVariants \
  -R refs/hs37d5.fa.gz \
  -V vcfs/1gk_all.vcf.gz \
  --keep-ids racist_hypothesis/data/20200319_snps_ibd_csl.list \
  -O vcfs/snphits_ibd.vcf.gz
  
gatk SelectVariants \
  -R refs/hs37d5.fa.gz \
  -V vcfs/1gk_all.vcf.gz \
  --keep-ids racist_hypothesis/data/20200319_snps_ibd.list \
  -O vcfs/snphits_ibd_full.vcf.gz

Copy VCFs for all into data folder

cp vcfs/snphits* racist_hypothesis/data

20200320

Analysis Proper

Import 1GK metadata (for population)

From here: http://ftp.1000genomes.ebi.ac.uk/vol1/ftp/technical/working/20130606_sample_info/20130606_sample_info.xlsx (link embedded in this page: <www.internationalgenome.org/data>)

meta <- read_xlsx(here::here("data", "20130606_sample_info.xlsx"), sheet = "Sample Info") %>% dplyr::select(Sample, Population, Gender)

Read in VCFs with allele counts for target SNPs

# set names
names(vcf_list) <- gsub("snphits_|.vcf.gz", "", list.files(here::here("data"), pattern = glob2rx("snphits_*.gz")))
Error in names(vcf_list) <- gsub("snphits_|.vcf.gz", "", list.files(here::here("data"),  : 
  'names' attribute [4] must be the same length as the vector [3]

Read in SNP data

NOTE:

• In the Lee et al. (edu_years) data tables, the sheet says: “Notes: Clumping of GWAS results was performed as described in the Supplementary Notes. SNPs are ordered by P-value. Chromosome (CHR) and base pair (BP) positions are reported for human genome build 37 (hg19). "Allele 1" is the allele whose estimated effect size (Beta) and allele frequency are reported. Standard errors (SE) and P-values are derived from test statistics that have been adjusted by an estimated LDSC intercept of 1.113. The analysis is based on 10,016,265 SNPs. The average chi-squared statistic is 2.530/2.816 (adjusted and unadjusted, respectively) and lambda GC is 2.038 (unadjusted).”

• In the Yengo et al. (height) data tables, the header is self-explanatory.

• In the Huang et al. (ibd) data tables, the SNPs have odds ratios rather than betas. Also, the legend says that A0 is the reference allele and A1 is the tested allele. p_multi is the -log10(P-value) for multi-variate model.

# create empty list
snps_list <- list()
# add edu_years SNPs
snps_list[["edu_years"]] <- read_xlsx(here::here("data", "20180723_Lee-et-al_supp-tables.xlsx"), sheet = "2. EduYears Lead SNPs", skip = 1, n_max = 1271) %>% 
  dplyr::select(snp = "SNP", 
                tested_allele = "Allele 1", 
                other_allele = "Allele2", 
                effect_size = "Effect size", 
                p_value = "P-value")
# add height SNPs
snps_list[["height"]] <- read_delim(here::here("data", "20181015_Yengo-et-al_snps_height.txt"), delim = "\t") %>% 
  dplyr::select(snp = "SNP", 
                tested_allele = "Tested_Allele", 
                other_allele = "Other_Allele", 
                effect_size = "BETA", 
                p_value = "P")
Parsed with column specification:
cols(
  SNP = col_character(),
  CHR = col_double(),
  POS = col_double(),
  Tested_Allele = col_character(),
  Other_Allele = col_character(),
  Freq_Tested_Allele_in_HRS = col_double(),
  BETA = col_double(),
  SE = col_double(),
  P = col_double(),
  N = col_double(),
  BETA_COJO = col_double(),
  SE_COJO = col_double(),
  P_COJO = col_double()
)
# add ibd SNPs (full list)
snps_list[["ibd_full"]] <- read_xlsx(here::here("data", "20170628_Huang-et-al_supp-table-1.xlsx"), sheet = "list of variants") %>% 
  dplyr::mutate(mean_or = (OR_CD + OR_UC) / 2) %>% 
  dplyr::select(snp = "variant", 
                tested_allele = "A1", 
                other_allele = "A0", 
                effect_size =  "mean_or",
                p_value = "P_mean_95")
New names:
* trait -> trait...7
* trait -> trait...8
# add ibd SNPS - note the "effect size" here is the mean odds ratio across both CD and UC
snps_list[["ibd"]] <- read_xlsx(here::here("data", "20170628_Huang-et-al_supp-table-1.xlsx"), sheet = "list of credible sets") %>% 
  dplyr::mutate(mean_or = (OR_CD + OR_UC) / 2) %>% 
  dplyr::select(snp = "variant.lead", 
                tested_allele = "A1", 
                other_allele = "A0", 
                effect_size =  "mean_or",
                p_value = "p_multi")

Filter out indels from full IBD list

# filter
snps_list[["ibd_full"]] <- dplyr::slice(snps_list[["ibd_full"]], (ibd_indels * -1))
Warning messages:
1: Unknown or uninitialised column: 'SNP'. 
2: Unknown or uninitialised column: 'SNP'. 
3: Unknown or uninitialised column: 'SNP'. 
4: Unknown or uninitialised column: 'SNP'. 

Create data frames with allele frequencies

# Incorporate into function:
get_alfreq_table <- function(population, snp_df){
  # take only the SNPs that are also in the snp_df
  indexes_to_keep <- which(names(population) %in% snp_df$snp == T)
  population <- population[indexes_to_keep]
  # start loop
  counter <- 0
  population <- lapply(population, function(snp_summary){
    # set counter and extract SNP ID
    counter <<- counter + 1
    snp_id <- names(population)[counter]
    # create final DF
    final_df <- data.frame("allele" = names(snp_summary$allele),
                           "count" = snp_summary$allele,
                           stringsAsFactors = F)
    # get allele frequency
    final_df$al_freq <- final_df$count / sum(final_df$count)
    # pull out effect sizes and p-values from snp_df
    effect_sizes <- data.frame("allele" = c(snp_df$other_allele[snp_df$snp == snp_id],
                                            snp_df$tested_allele[snp_df$snp == snp_id]),
                               "effect_size" = c(0, snp_df$effect_size[snp_df$snp == snp_id]),
                               "p_value" = c(0, snp_df$p_value[snp_df$snp == snp_id]),
                               stringsAsFactors = F)
    # join DFs. Note that it only joins the two alleles in the snp_df
    final_df <- dplyr::left_join(effect_sizes, final_df, by = "allele")
    # remove any rows with NA (caused by having a third allele)
    final_df <- final_df[complete.cases(final_df), ]
    # convert negative effects sizes into positive ones by flipping the allele
    if(any(final_df$effect_size < 0)){
      final_df$effect_size[final_df$effect_size == 0] <- final_df$effect_size[final_df$effect_size < 0] * (-1)
      final_df$effect_size[final_df$effect_size < 0] <- 0
    }
    return(final_df)
  })
  return(population)
}
Warning message:
Unknown or uninitialised column: 'SNP'. 

Create columns for each population

plot_df_lst <- lapply(alcnt_df_filt, function(pheno){
  pheno <- pheno %>% dplyr::select(-count)
  pheno <- tidyr::pivot_wider(data = pheno,
                              names_from = population,
                              names_prefix = "al_freq_",
                              values_from = al_freq)
  return(pheno)
})

Plot

Set up vectors for phenotype-specific parameters

# Colour palettes
colour_pals <- c("viridis", "inferno", "plasma")
# Line colours
#line_cols <- c("red", "red", "blue")
# Titles
titles <- c("Educational Attainment", "Height", "Inflammatory Bowel Disease")
# Legend title for effect size
legend_title <- c("Effect size (Beta)", "Effect size (Beta)", "Mean odds ratio (CD and UC)")

YRI v CEU

counter <- 0
Warning messages:
1: Unknown or uninitialised column: 'SNP'. 
2: Unknown or uninitialised column: 'SNP'. 
3: Unknown or uninitialised column: 'SNP'. 
4: Unknown or uninitialised column: 'SNP'. 
lapply(plot_df_lst, function(pheno){
  counter <<- counter + 1
  ggplot(pheno, aes(al_freq_YRI, al_freq_CEU, colour = effect_size)) +
    geom_point() +
    scale_color_viridis_c(option = colour_pals[counter]) +
    coord_fixed() +
    geom_smooth(se = F, colour = line_cols[counter]) +
    geom_abline(intercept = 0, slope = 1, colour = "blue") +
    xlab("Allele frequency in YRI") +
    ylab("Allele frequency in CEU") +
    labs(title = titles[counter],
         colour = legend_title[counter])
})
$eduyrs

$height

$ibd_full

YRI v CHS

counter <- 0
lapply(plot_df_lst, function(pheno){
  counter <<- counter + 1
  ggplot(pheno, aes(al_freq_YRI, al_freq_CHS, colour = effect_size)) +
    geom_point() +
    scale_color_viridis_c(option = colour_pals[counter]) +
    coord_fixed() +
    geom_smooth(se = F, colour = line_cols[counter]) +
    geom_abline(intercept = 0, slope = 1, colour = "blue") +
    xlab("Allele frequency in YRI") +
    ylab("Allele frequency in CHS") +
    labs(title = titles[counter],
         colour = legend_title[counter])
})
$eduyrs

$height

$ibd_full

Fst

Create data frames

# Try without population column
vcf_list_raw <- lapply(target_vcfs, function(vcf_file){
  vcf_out <- pegas::read.vcf(vcf_file)
})

Reading 100 / 10000 loci
Reading 200 / 10000 loci
Reading 300 / 10000 loci
Reading 400 / 10000 loci
Reading 500 / 10000 loci
Reading 600 / 10000 loci
Reading 700 / 10000 loci
Reading 800 / 10000 loci
Reading 900 / 10000 loci
Reading 1000 / 10000 loci
Reading 1100 / 10000 loci
Reading 1200 / 10000 loci
Reading 1270 / 1270 loci.
Done.

Reading 100 / 10000 loci
Reading 200 / 10000 loci
Reading 300 / 10000 loci
Reading 400 / 10000 loci
Reading 500 / 10000 loci
Reading 600 / 10000 loci
Reading 700 / 10000 loci
Reading 800 / 10000 loci
Reading 900 / 10000 loci
Reading 1000 / 10000 loci
Reading 1100 / 10000 loci
Reading 1200 / 10000 loci
Reading 1300 / 10000 loci
Reading 1400 / 10000 loci
Reading 1500 / 10000 loci
Reading 1600 / 10000 loci
Reading 1700 / 10000 loci
Reading 1800 / 10000 loci
Reading 1900 / 10000 loci
Reading 2000 / 10000 loci
Reading 2100 / 10000 loci
Reading 2200 / 10000 loci
Reading 2300 / 10000 loci
Reading 2400 / 10000 loci
Reading 2500 / 10000 loci
Reading 2600 / 10000 loci
Reading 2700 / 10000 loci
Reading 2800 / 10000 loci
Reading 2900 / 10000 loci
Reading 3000 / 10000 loci
Reading 3100 / 10000 loci
Reading 3200 / 10000 loci
Reading 3277 / 3277 loci.
Done.

Reading 100 / 10000 loci
Reading 200 / 10000 loci
Reading 300 / 10000 loci
Reading 400 / 10000 loci
Reading 500 / 10000 loci
Reading 600 / 10000 loci
Reading 700 / 10000 loci
Reading 800 / 10000 loci
Reading 900 / 10000 loci
Reading 1000 / 10000 loci
Reading 1100 / 10000 loci
Reading 1200 / 10000 loci
Reading 1300 / 10000 loci
Reading 1400 / 10000 loci
Reading 1500 / 10000 loci
Reading 1600 / 10000 loci
Reading 1700 / 10000 loci
Reading 1800 / 10000 loci
Reading 1900 / 10000 loci
Reading 2000 / 10000 loci
Reading 2100 / 10000 loci
Reading 2200 / 10000 loci
Reading 2300 / 10000 loci
Reading 2400 / 10000 loci
Reading 2500 / 10000 loci
Reading 2600 / 10000 loci
Reading 2700 / 10000 loci
Reading 2800 / 10000 loci
Reading 2900 / 10000 loci
Reading 3000 / 10000 loci
Reading 3100 / 10000 loci
Reading 3200 / 10000 loci
Reading 3300 / 10000 loci
Reading 3400 / 10000 loci
Reading 3500 / 10000 loci
Reading 3600 / 10000 loci
Reading 3700 / 10000 loci
Reading 3800 / 10000 loci
Reading 3900 / 10000 loci
Reading 4000 / 10000 loci
Reading 4039 / 4039 loci.
Done.

Plot

ggplot(fst_out_df, aes(Fst, fill = phenotype)) +
  geom_density(alpha = 0.7) +
  labs(fill = "Phenotype") +
  ylab("Density") +
  theme_bw()
Warning message:
Unknown or uninitialised column: 'SNP'. 

20200330

From EB: “We need a stronger positive control - perhaps can you get skin pigmentation loci?”

Re-do with skin pigmentation positive control

Find papers with associated SNPs

Crawford et al. (2017) Loci associated with skin pigmentation identified in African populations, Science: https://science.sciencemag.org/content/358/6365/eaan8433.abstract?casa_token=6tGEjct1nUQAAAAA:IL7LCz-xQ9l6rLhxBk5VcGBjwTrEa5UpAlC-nCl2mvcASu4iZbWMiu_Uj8YUHIISgCibOd1ya25sOQ. Take table 1, and manually transform ‘Ancestral>Divided’ column into new columns titled ‘tested_allele’ (the allele in bold) and ‘other_allele’. Save here: data/20171117_Crawford-et-al_Table-1.xlsxAdhikari et al. (2019) A GWAS in Latin Americans highlights the convergent evolution of lighter skin pigmentation in Eurasia, Nature: https://www.nature.com/articles/s41467-018-08147-0. “Summary statistics from the GWAS analyses is deposited at GWAS central with the link http://www.gwascentral.org/study/HGVST3308”. Under the ‘Association results’ tab, there is one dataset for each of the 6 phenotypes tested:
- Melanin index - Hair color - Eye color - Digital eye color: L (lightness) - Digital eye color: C (chroma) - Digital eye color: cosH (cosine of hue) Difficult to ascertain which ones had genome-wide significance. Instead, pull tables directly from paper and supplementary materials, and put here in different sheets: data/20190121_Adhikari-et-al_snps.xlsx - Table 1: 18 lead SNPs from paper, each with a different p-value for one of the 6 phenotypes. - supp_table_6: 11 SNPs associated with combined traits. - supp_table_12: 161 SNPs collagted from published association studies on pigmentation. See table for references, which were used to identify other pigmentation GWAS studies. • Hernandez-Pacheco et al. (2017) Identification of a novel locus associated with skin colour in African-admixed populations, Scientific Reports: https://www.nature.com/articles/srep44548. 9 hits with genome-wide significance here: data/20170316_Hernandez-Pacheco-et-al.xlsx.

Others compiled into the single XLSX doc data/20200622_pigmentation_snps.xlsx: • ‘20190321_Jonnalagadda-et-al’: Jonnalagadda et al. (2019) A Genome-Wide Association Study of Skin and Iris Pigmentation among Individuals of South Asian Ancestry, Genome Biology and Evolution: https://academic.oup.com/gbe/article/11/4/1066/5416147. Took 9 SNPs associated with iris colour from Table 1, and 14 SNPs described in previous studies from Table 2. • ‘20171130_Martin-et-al’: Martin et al. (2017) An Unexpectedly Complex Architecture for Skin Pigmentation in Africans, Cell: https://www.cell.com/cell/fulltext/S0092-8674%2817%2931324-7. Took all 42 SNPs from Table S6A in Supplemental Information here: https://www.cell.com/cms/10.1016/j.cell.2017.11.015/attachment/eccead83-1a96-4444-9032-f968ee481d15/mmc2.xlsx. • ‘20150512_Liu-et-al’: Liu et al. (2015) Genetics of skin color variation in Europeans: genome-wide association studies with functional follow-up, Human Genetics: https://link.springer.com/article/10.1007%2Fs00439-015-1559-0. Took all 9 SNPs from Table 1. • ‘20121031_Candille-et-al’: Candille et al. (2012) Genome-wide association studies of quantitatively measured skin, hair, and eye pigmentation in four European populations, PLoS One: https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0048294. Took all 8 SNPs from Table 2. • ‘20100614_Gerstenblith-et-al’: Gerstenblith et al. (2010) Genome-wide association studies of pigmentation and skin cancer: a review and meta-analysis, Pigment Cell Melanoma Research: https://onlinelibrary.wiley.com/doi/full/10.1111/j.1755-148X.2010.00730.x. Took all 39 SNPs from Table 4. • ‘20080516_Han-et-al’: Han et al. (2008) A Genome-Wide Association Study Identifies Novel Alleles Associated with Hair Color and Skin Pigmentation, PLOS Genetics: https://journals.plos.org/plosgenetics/article?id=10.1371/journal.pgen.1000074. Took all 38 SNPs from Table 2.

Create list of target SNPs

pig_snps <- list()
# Crawford
pig_snps[["crawford"]] <- readxl::read_excel(here("data", "20171117_Crawford-et-al_Table-1.xlsx")) %>% 
  dplyr::select(rsid = "RSID", everything()) %>% 
  dplyr::filter(!is.na(rsid),
                !rsid == ".")

# Adhikari
pig_snps[["adhikari_tbl_1"]] <- readxl::read_excel(here("data", "20190121_Adhikari-et-al_snps.xlsx"),
                                                   sheet = "Table 1",
                                                   skip = 1) %>% 
  dplyr::select(rsid = "rsID", everything()) %>% 
  dplyr::filter(!is.na(rsid))

pig_snps[["adhikari_supp_6"]] <- readxl::read_excel(here("data", "20190121_Adhikari-et-al_snps.xlsx"),
                                                   sheet = "supp_table_6") %>% 
  dplyr::select(rsid = "SNP", everything()) %>% 
  dplyr::filter(!is.na(rsid))  

pig_snps[["adhikari_supp_12"]] <- readxl::read_excel(here("data", "20190121_Adhikari-et-al_snps.xlsx"),
                                                   sheet = "supp_table_12") %>% 
  dplyr::select(rsid = "SNP", everything()) %>% 
  dplyr::filter(!is.na(rsid))

# Hernandez-Pacheco
pig_snps[["hernandez-pacheco"]] <- readxl::read_excel(here("data", "20170316_Hernandez-Pacheco-et-al.xlsx"))

# Doc with SNPs from multiple studies
sheet_names <- readxl::excel_sheets(here("data", "20200622_pigmentation_snps.xlsx"))
compiled_snps <- lapply(sheet_names, function(x){
  x <- readxl::read_excel(here("data", "20200622_pigmentation_snps.xlsx"),
                     sheet = x)
})
names(compiled_snps) <- sheet_names

# Combine
pig_snps <- c(pig_snps, compiled_snps)

Get summary stats

# How many total SNPs
sum(unlist(lapply(pig_snps, nrow)))
[1] 414

Pull out unique SNPs

pig_df <- lapply(pig_snps, function(x) dplyr::select(x, rsid))
pig_df <- dplyr::bind_rows(pig_df)
pig_df <- unique(pig_df)

nrow(pig_df)
[1] 266

Write table

write.table(x = pig_df$rsid,
            file = here::here("data", "20200622_snps_pig.list"),
            quote = F,
            row.names = F,
            col.names = F)

Extract calls for those SNPs from VCF

gatk SelectVariants \
  -R refs/hs37d5.fa.gz \
  -V vcfs/1gk_all.vcf.gz \
  --keep-ids racist_hypothesis/data/20200622_snps_pig.list \
  -O vcfs/snphits_pig.vcf.gz

Copy back to data folder

cp vcfs/snphits_pig.vcf.gz* racist_hypothesis/data/

Get metadata

meta <- read_xlsx(here::here("data", "20130606_sample_info.xlsx"), sheet = "Sample Info") %>% dplyr::select(Sample, Population, Gender)

Read in VCFs and obtain allele counts for target SNPs

# read in VCFs and get allele counts
vcf_list <- lapply(target_vcfs, function(vcf_file){
  # read in VCFs
  vcf_out <- pegas::read.vcf(vcf_file)
  # create population column
  populations <- unlist(lapply(rownames(vcf_out), function(sample){
    meta$Population[meta$Sample == sample]
  }))
  vcf_out$population <- populations
  # reorder
  vcf_out <- vcf_out %>% dplyr::select(population, everything())
  # split by population
  vcf_out_split <- split(vcf_out, f = vcf_out$population)
  # remove population columns
  vcf_out_split <- lapply(vcf_out_split, function(x){
    x$population <- NULL
    return(x)
  })
#  # get allele counts
#  allele_counts <- lapply(vcf_out_split, function(population){
#    summary(population)
#  })
#  return(allele_counts)
})
File apparently not yet accessed:
Scanning file /Users/brettell/Documents/Repositories/racist_hypothesis/data/snphits_eduyrs.vcf.gz 

 12.97124 Mb
Done.

Reading 100 / 10000 loci
Reading 200 / 10000 loci
Reading 300 / 10000 loci
Reading 400 / 10000 loci
Reading 500 / 10000 loci
Reading 600 / 10000 loci
Reading 700 / 10000 loci
Reading 800 / 10000 loci
Reading 900 / 10000 loci
Reading 1000 / 10000 loci
Reading 1100 / 10000 loci
Reading 1200 / 10000 loci
Reading 1270 / 1270 loci.
Done.
File apparently not yet accessed:
Scanning file /Users/brettell/Documents/Repositories/racist_hypothesis/data/snphits_height.vcf.gz 

 33.40984 Mb
Done.

Reading 100 / 10000 loci
Reading 200 / 10000 loci
Reading 300 / 10000 loci
Reading 400 / 10000 loci
Reading 500 / 10000 loci
Reading 600 / 10000 loci
Reading 700 / 10000 loci
Reading 800 / 10000 loci
Reading 900 / 10000 loci
Reading 1000 / 10000 loci
Reading 1100 / 10000 loci
Reading 1200 / 10000 loci
Reading 1300 / 10000 loci
Reading 1400 / 10000 loci
Reading 1500 / 10000 loci
Reading 1600 / 10000 loci
Reading 1700 / 10000 loci
Reading 1800 / 10000 loci
Reading 1900 / 10000 loci
Reading 2000 / 10000 loci
Reading 2100 / 10000 loci
Reading 2200 / 10000 loci
Reading 2300 / 10000 loci
Reading 2400 / 10000 loci
Reading 2500 / 10000 loci
Reading 2600 / 10000 loci
Reading 2700 / 10000 loci
Reading 2800 / 10000 loci
Reading 2900 / 10000 loci
Reading 3000 / 10000 loci
Reading 3100 / 10000 loci
Reading 3200 / 10000 loci
Reading 3277 / 3277 loci.
Done.
File apparently not yet accessed:
Scanning file /Users/brettell/Documents/Repositories/racist_hypothesis/data/snphits_pig_full.vcf.gz 

 41.17255 Mb
Done.

Reading 100 / 10000 loci
Reading 200 / 10000 loci
Reading 300 / 10000 loci
Reading 400 / 10000 loci
Reading 500 / 10000 loci
Reading 600 / 10000 loci
Reading 700 / 10000 loci
Reading 800 / 10000 loci
Reading 900 / 10000 loci
Reading 1000 / 10000 loci
Reading 1100 / 10000 loci
Reading 1200 / 10000 loci
Reading 1300 / 10000 loci
Reading 1400 / 10000 loci
Reading 1500 / 10000 loci
Reading 1600 / 10000 loci
Reading 1700 / 10000 loci
Reading 1800 / 10000 loci
Reading 1900 / 10000 loci
Reading 2000 / 10000 loci
Reading 2100 / 10000 loci
Reading 2200 / 10000 loci
Reading 2300 / 10000 loci
Reading 2400 / 10000 loci
Reading 2500 / 10000 loci
Reading 2600 / 10000 loci
Reading 2700 / 10000 loci
Reading 2800 / 10000 loci
Reading 2900 / 10000 loci
Reading 3000 / 10000 loci
Reading 3100 / 10000 loci
Reading 3200 / 10000 loci
Reading 3300 / 10000 loci
Reading 3400 / 10000 loci
Reading 3500 / 10000 loci
Reading 3600 / 10000 loci
Reading 3700 / 10000 loci
Reading 3800 / 10000 loci
Reading 3900 / 10000 loci
Reading 4000 / 10000 loci
Reading 4039 / 4039 loci.
Done.

Not working well. Try vcfR

test <- vcfR::read.vcfR(target_vcfs[1])
Scanning file to determine attributes.
File attributes:
  meta lines: 258
  header_line: 259
  variant count: 1270
  column count: 2513

Meta line 258 read in.
All meta lines processed.
gt matrix initialized.
Character matrix gt created.
  Character matrix gt rows: 1270
  Character matrix gt cols: 2513
  skip: 0
  nrows: 1270
  row_num: 0

Processed variant 1000
Processed variant: 1270
All variants processed
The following INFO key occurred more than once: AC 
The following INFO key occurred more than once: AF 
The following INFO key occurred more than once: DP 

Set up vectors for phenotype-specific parameters

Plot

YRI v CEU

counter <- 0
There were 12 warnings (use warnings() to see them)
lapply(al_freq_df, function(pheno){
  counter <<- counter + 1
  ggplot(pheno,
         aes(YRI, CEU)) +
    geom_point() +
#    scale_color_viridis_c(option = colour_pals[counter]) +
    coord_fixed() +
    geom_smooth(se = F, colour = "red") +
    geom_abline(intercept = 0, slope = 1, colour = "blue") +
    xlab("Allele frequency in YRI") +
    ylab("Allele frequency in CEU") +
    labs(title = titles[counter])
})
$edu

$hei

$pig

YRI v CHS

counter <- 0
There were 15 warnings (use warnings() to see them)
lapply(al_freq_df, function(pheno){
  counter <<- counter + 1
  ggplot(pheno,
         aes(YRI, CHS)) +
    geom_point() +
#    scale_color_viridis_c(option = colour_pals[counter]) +
    coord_fixed() +
    geom_smooth(se = F, colour = "red") +
    geom_abline(intercept = 0, slope = 1, colour = "blue") +
    xlab("Allele frequency in YRI") +
    ylab("Allele frequency in CHS") +
    labs(title = titles[counter])
})
$edu

$hei

$pig

Do again after randomly swapping minor allele

set.seed(65)
rdm_sds <- sample(1:100, 3)

counter <- 0
al_freq_df_shuff <- lapply(al_freq_df, function(pheno){
  counter <<- counter + 1
  # set seed
  set.seed(rdm_sds[counter])
  # select SNPs to swap (half of total)
  tgt_indcs <- sample(nrow(pheno), nrow(pheno) /2)
  # swap minor alleles
  pheno[tgt_indcs, 5:ncol(pheno)] <- 1 - pheno[tgt_indcs, 5:ncol(pheno)]
  # return pheno
  return(pheno)
})
counter <- 0
lapply(al_freq_df_shuff, function(pheno){
  counter <<- counter + 1
  ggplot(pheno,
         aes(YRI, CHS)) +
    geom_point() +
#    scale_color_viridis_c(option = colour_pals[counter]) +
    coord_fixed() +
    geom_smooth(se = F, colour = "red") +
    geom_abline(intercept = 0, slope = 1, colour = "blue") +
    xlab("Allele frequency in YRI") +
    ylab("Allele frequency in CHS") +
    labs(title = titles[counter])
})
$edu

$hei

$pig

Fst

target_vcfs
[1] "/Users/brettell/Documents/Repositories/racist_hypothesis/data/snphits_eduyrs.vcf.gz"
[2] "/Users/brettell/Documents/Repositories/racist_hypothesis/data/snphits_height.vcf.gz"
[3] "/Users/brettell/Documents/Repositories/racist_hypothesis/data/snphits_pig.vcf.gz"   

Create data frames

# Create raw list of variants
vcf_list_raw <- lapply(target_vcfs, function(vcf_file){
  vcf_out <- pegas::read.vcf(vcf_file)
})

Reading 100 / 10000 loci
Reading 200 / 10000 loci
Reading 300 / 10000 loci
Reading 400 / 10000 loci
Reading 500 / 10000 loci
Reading 600 / 10000 loci
Reading 700 / 10000 loci
Reading 800 / 10000 loci
Reading 900 / 10000 loci
Reading 1000 / 10000 loci
Reading 1100 / 10000 loci
Reading 1200 / 10000 loci
Reading 1270 / 1270 loci.
Done.

Reading 100 / 10000 loci
Reading 200 / 10000 loci
Reading 300 / 10000 loci
Reading 400 / 10000 loci
Reading 500 / 10000 loci
Reading 600 / 10000 loci
Reading 700 / 10000 loci
Reading 800 / 10000 loci
Reading 900 / 10000 loci
Reading 1000 / 10000 loci
Reading 1100 / 10000 loci
Reading 1200 / 10000 loci
Reading 1300 / 10000 loci
Reading 1400 / 10000 loci
Reading 1500 / 10000 loci
Reading 1600 / 10000 loci
Reading 1700 / 10000 loci
Reading 1800 / 10000 loci
Reading 1900 / 10000 loci
Reading 2000 / 10000 loci
Reading 2100 / 10000 loci
Reading 2200 / 10000 loci
Reading 2300 / 10000 loci
Reading 2400 / 10000 loci
Reading 2500 / 10000 loci
Reading 2600 / 10000 loci
Reading 2700 / 10000 loci
Reading 2800 / 10000 loci
Reading 2900 / 10000 loci
Reading 3000 / 10000 loci
Reading 3100 / 10000 loci
Reading 3200 / 10000 loci
Reading 3277 / 3277 loci.
Done.
File apparently not yet accessed:
Scanning file /Users/brettell/Documents/Repositories/racist_hypothesis/data/snphits_pig.vcf.gz 

 2.696805 Mb
Done.

Reading 100 / 10000 loci
Reading 200 / 10000 loci
Reading 261 / 261 loci.
Done.
# Create vector of populations
populations <- unlist(lapply(rownames(vcf_list_raw[[1]]), function(sample){
  meta$Population[meta$Sample == sample]
}))

# Generate Fst stats
fst_out_lst <- lapply(vcf_list_raw, function(pheno){
  as.data.frame(pegas::Fst(pheno, pop = populations))
})

# make rownames into separate column
fst_out_lst <- lapply(fst_out_lst, function(pheno){
  pheno$snp <- rownames(pheno)
  return(pheno)
})
names(fst_out_lst) <- titles

# bind into single DF
fst_out_df <- dplyr::bind_rows(fst_out_lst, .id = "phenotype")
head(fst_out_df)

Plot density

ggplot(fst_out_df, aes(Fst, fill = phenotype)) +
  geom_density(alpha = 0.7) +
  labs(fill = "Phenotype") +
  ylab("Density") +
  theme_bw() +
  scale_fill_manual(values = c("#00AFBB", "#E7B800", "#FC4E07"))

Try with just YRI, CEU, and CHS

# get samples from target popns only
target_popns <- which(populations %in% c("YRI", "CEU", "CHS"))
populations_3pop <- populations[target_popns]

vcf_list_raw_3pop <- lapply(vcf_list_raw, function(pheno){
  pheno[target_popns, ]
})

# Generate Fst stats
fst_out_lst_3pop <- lapply(vcf_list_raw_3pop, function(pheno){
  as.data.frame(pegas::Fst(pheno, pop = populations_3pop))
})

# make rownames into separate column
fst_out_lst_3pop <- lapply(fst_out_lst_3pop, function(pheno){
  pheno$snp <- rownames(pheno)
  return(pheno)
})
names(fst_out_lst_3pop) <- titles

# bind into single DF
fst_out_df_3pop <- dplyr::bind_rows(fst_out_lst_3pop, .id = "phenotype")
head(fst_out_df_3pop)

Plot

ggplot(fst_out_df_3pop, aes(Fst, fill = phenotype)) +
There were 16 warnings (use warnings() to see them)
  geom_density(alpha = 0.7) +
  labs(fill = "Phenotype") +
  ylab("Density") +
  theme_bw() +
  scale_fill_manual(values = c("#00AFBB", "#E7B800", "#FC4E07"))

Try with ridges

# factorise 
fst_out_df_3pop$phenotype <- factor(fst_out_df_3pop$phenotype,
                                    levels = c("Pigmentation", "Height", "Educational Attainment"))

ggplot() +
  geom_density_ridges2(data = fst_out_df_3pop,
                       mapping = aes(x = Fst, y = phenotype, fill = phenotype),
                       scale = 0.5,
                       alpha = 0.8) +
  scale_fill_manual(values = c("#FC4E07", "#00AFBB", "#E7B800")) +
  ylab(label = NULL) +
  theme_bw() #+

  theme_ridges(center = T)
List of 93
 $ line                      :List of 6
  ..$ colour       : chr "black"
  ..$ size         : num 0.636
  ..$ linetype     : num 1
  ..$ lineend      : chr "butt"
  ..$ arrow        : logi FALSE
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_line" "element"
 $ rect                      :List of 5
  ..$ fill         : chr "transparent"
  ..$ colour       : logi NA
  ..$ size         : num 0
  ..$ linetype     : num 0
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_rect" "element"
 $ text                      :List of 11
  ..$ family       : chr ""
  ..$ face         : chr "plain"
  ..$ colour       : chr "black"
  ..$ size         : num 14
  ..$ hjust        : num 0.5
  ..$ vjust        : num 0.5
  ..$ angle        : num 0
  ..$ lineheight   : num 0.9
  ..$ margin       : 'margin' num [1:4] 0points 0points 0points 0points
  .. ..- attr(*, "unit")= int 8
  ..$ debug        : logi FALSE
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ title                     : NULL
 $ aspect.ratio              : NULL
 $ axis.title                : NULL
 $ axis.title.x              :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : NULL
  ..$ hjust        : num 0.5
  ..$ vjust        : NULL
  ..$ angle        : NULL
  ..$ lineheight   : NULL
  ..$ margin       : 'margin' num [1:4] 6points 0points 3points 0points
  .. ..- attr(*, "unit")= int 8
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ axis.title.x.top          :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : NULL
  ..$ hjust        : NULL
  ..$ vjust        : num 0
  ..$ angle        : NULL
  ..$ lineheight   : NULL
  ..$ margin       : 'margin' num [1:4] 0points 0points 3.5points 0points
  .. ..- attr(*, "unit")= int 8
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ axis.title.x.bottom       : NULL
 $ axis.title.y              :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : NULL
  ..$ hjust        : num 0.5
  ..$ vjust        : NULL
  ..$ angle        : num 90
  ..$ lineheight   : NULL
  ..$ margin       : 'margin' num [1:4] 0points 6points 0points 3points
  .. ..- attr(*, "unit")= int 8
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ axis.title.y.left         : NULL
 $ axis.title.y.right        :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : NULL
  ..$ hjust        : NULL
  ..$ vjust        : num 0
  ..$ angle        : num -90
  ..$ lineheight   : NULL
  ..$ margin       : 'margin' num [1:4] 0points 0points 0points 3.5points
  .. ..- attr(*, "unit")= int 8
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ axis.text                 :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : chr "black"
  ..$ size         : num 12
  ..$ hjust        : NULL
  ..$ vjust        : NULL
  ..$ angle        : NULL
  ..$ lineheight   : NULL
  ..$ margin       : NULL
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ axis.text.x               :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : NULL
  ..$ hjust        : NULL
  ..$ vjust        : num 1
  ..$ angle        : NULL
  ..$ lineheight   : NULL
  ..$ margin       : 'margin' num [1:4] 3points 0points 0points 0points
  .. ..- attr(*, "unit")= int 8
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ axis.text.x.top           :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : NULL
  ..$ hjust        : NULL
  ..$ vjust        : num 0
  ..$ angle        : NULL
  ..$ lineheight   : NULL
  ..$ margin       : 'margin' num [1:4] 0points 0points 2.8points 0points
  .. ..- attr(*, "unit")= int 8
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ axis.text.x.bottom        : NULL
 $ axis.text.y               :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : NULL
  ..$ hjust        : num 1
  ..$ vjust        : num 0
  ..$ angle        : NULL
  ..$ lineheight   : NULL
  ..$ margin       : 'margin' num [1:4] 0points 3points 0points 0points
  .. ..- attr(*, "unit")= int 8
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ axis.text.y.left          : NULL
 $ axis.text.y.right         :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : NULL
  ..$ hjust        : num 0
  ..$ vjust        : NULL
  ..$ angle        : NULL
  ..$ lineheight   : NULL
  ..$ margin       : 'margin' num [1:4] 0points 0points 0points 2.8points
  .. ..- attr(*, "unit")= int 8
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ axis.ticks                :List of 6
  ..$ colour       : chr "grey90"
  ..$ size         : num 0.5
  ..$ linetype     : NULL
  ..$ lineend      : NULL
  ..$ arrow        : logi FALSE
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_line" "element"
 $ axis.ticks.x              : NULL
 $ axis.ticks.x.top          : NULL
 $ axis.ticks.x.bottom       : NULL
 $ axis.ticks.y              :List of 6
  ..$ colour       : chr "grey90"
  ..$ size         : num 0.5
  ..$ linetype     : NULL
  ..$ lineend      : NULL
  ..$ arrow        : logi FALSE
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_line" "element"
 $ axis.ticks.y.left         : NULL
 $ axis.ticks.y.right        : NULL
 $ axis.ticks.length         : 'simpleUnit' num 3.5points
  ..- attr(*, "unit")= int 8
 $ axis.ticks.length.x       : NULL
 $ axis.ticks.length.x.top   : NULL
 $ axis.ticks.length.x.bottom: NULL
 $ axis.ticks.length.y       : NULL
 $ axis.ticks.length.y.left  : NULL
 $ axis.ticks.length.y.right : NULL
 $ axis.line                 : list()
  ..- attr(*, "class")= chr [1:2] "element_blank" "element"
 $ axis.line.x               : NULL
 $ axis.line.x.top           : NULL
 $ axis.line.x.bottom        : NULL
 $ axis.line.y               : NULL
 $ axis.line.y.left          : NULL
 $ axis.line.y.right         : NULL
 $ legend.background         :List of 5
  ..$ fill         : NULL
  ..$ colour       : logi NA
  ..$ size         : NULL
  ..$ linetype     : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_rect" "element"
 $ legend.margin             : 'margin' num [1:4] 7points 7points 7points 7points
  ..- attr(*, "unit")= int 8
 $ legend.spacing            : 'simpleUnit' num 14points
  ..- attr(*, "unit")= int 8
 $ legend.spacing.x          : NULL
 $ legend.spacing.y          : NULL
 $ legend.key                : list()
  ..- attr(*, "class")= chr [1:2] "element_blank" "element"
 $ legend.key.size           : 'simpleUnit' num 1lines
  ..- attr(*, "unit")= int 3
 $ legend.key.height         : NULL
 $ legend.key.width          : NULL
 $ legend.text               :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : 'rel' num 0.857
  ..$ hjust        : NULL
  ..$ vjust        : NULL
  ..$ angle        : NULL
  ..$ lineheight   : NULL
  ..$ margin       : NULL
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ legend.text.align         : NULL
 $ legend.title              :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : NULL
  ..$ hjust        : num 0
  ..$ vjust        : NULL
  ..$ angle        : NULL
  ..$ lineheight   : NULL
  ..$ margin       : NULL
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ legend.title.align        : NULL
 $ legend.position           : chr "right"
 $ legend.direction          : NULL
 $ legend.justification      : chr [1:2] "left" "center"
 $ legend.box                : NULL
 $ legend.box.just           : NULL
 $ legend.box.margin         : 'margin' num [1:4] 0cm 0cm 0cm 0cm
  ..- attr(*, "unit")= int 1
 $ legend.box.background     : list()
  ..- attr(*, "class")= chr [1:2] "element_blank" "element"
 $ legend.box.spacing        : 'simpleUnit' num 14points
  ..- attr(*, "unit")= int 8
 $ panel.background          : list()
  ..- attr(*, "class")= chr [1:2] "element_blank" "element"
 $ panel.border              : list()
  ..- attr(*, "class")= chr [1:2] "element_blank" "element"
 $ panel.spacing             : 'simpleUnit' num 7points
  ..- attr(*, "unit")= int 8
 $ panel.spacing.x           : NULL
 $ panel.spacing.y           : NULL
 $ panel.grid                :List of 6
  ..$ colour       : chr "white"
  ..$ size         : NULL
  ..$ linetype     : NULL
  ..$ lineend      : NULL
  ..$ arrow        : logi FALSE
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_line" "element"
 $ panel.grid.major          :List of 6
  ..$ colour       : chr "grey90"
  ..$ size         : num 0.5
  ..$ linetype     : NULL
  ..$ lineend      : NULL
  ..$ arrow        : logi FALSE
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_line" "element"
 $ panel.grid.minor          : list()
  ..- attr(*, "class")= chr [1:2] "element_blank" "element"
 $ panel.grid.major.x        : NULL
 $ panel.grid.major.y        : NULL
 $ panel.grid.minor.x        : NULL
 $ panel.grid.minor.y        : NULL
 $ panel.ontop               : logi FALSE
 $ plot.background           : list()
  ..- attr(*, "class")= chr [1:2] "element_blank" "element"
 $ plot.title                :List of 11
  ..$ family       : NULL
  ..$ face         : chr "bold"
  ..$ colour       : NULL
  ..$ size         : num 14
  ..$ hjust        : num 0
  ..$ vjust        : NULL
  ..$ angle        : NULL
  ..$ lineheight   : NULL
  ..$ margin       : 'margin' num [1:4] 0points 0points 7points 0points
  .. ..- attr(*, "unit")= int 8
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ plot.title.position       : chr "panel"
 $ plot.subtitle             :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : 'rel' num 0.857
  ..$ hjust        : num 0
  ..$ vjust        : num 1
  ..$ angle        : NULL
  ..$ lineheight   : NULL
  ..$ margin       : 'margin' num [1:4] 0points 0points 6points 0points
  .. ..- attr(*, "unit")= int 8
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ plot.caption              :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : 'rel' num 0.857
  ..$ hjust        : num 1
  ..$ vjust        : num 1
  ..$ angle        : NULL
  ..$ lineheight   : NULL
  ..$ margin       : 'margin' num [1:4] 6points 0points 0points 0points
  .. ..- attr(*, "unit")= int 8
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ plot.caption.position     : chr "panel"
 $ plot.tag                  :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : 'rel' num 1.2
  ..$ hjust        : num 0.5
  ..$ vjust        : num 0.5
  ..$ angle        : NULL
  ..$ lineheight   : NULL
  ..$ margin       : NULL
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ plot.tag.position         : chr "topleft"
 $ plot.margin               : 'margin' num [1:4] 7points 14points 7points 7points
  ..- attr(*, "unit")= int 8
 $ strip.background          :List of 5
  ..$ fill         : chr "grey80"
  ..$ colour       : chr "grey50"
  ..$ size         : num 0
  ..$ linetype     : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_rect" "element"
 $ strip.background.x        : NULL
 $ strip.background.y        : NULL
 $ strip.placement           : chr "inside"
 $ strip.text                :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : 'rel' num 0.857
  ..$ hjust        : NULL
  ..$ vjust        : NULL
  ..$ angle        : NULL
  ..$ lineheight   : NULL
  ..$ margin       : NULL
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ strip.text.x              : NULL
 $ strip.text.y              :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : NULL
  ..$ hjust        : NULL
  ..$ vjust        : NULL
  ..$ angle        : num -90
  ..$ lineheight   : NULL
  ..$ margin       : NULL
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 $ strip.switch.pad.grid     : 'simpleUnit' num 3.5points
  ..- attr(*, "unit")= int 8
 $ strip.switch.pad.wrap     : 'simpleUnit' num 3.5points
  ..- attr(*, "unit")= int 8
 $ strip.text.y.left         :List of 11
  ..$ family       : NULL
  ..$ face         : NULL
  ..$ colour       : NULL
  ..$ size         : NULL
  ..$ hjust        : NULL
  ..$ vjust        : NULL
  ..$ angle        : num 90
  ..$ lineheight   : NULL
  ..$ margin       : NULL
  ..$ debug        : NULL
  ..$ inherit.blank: logi TRUE
  ..- attr(*, "class")= chr [1:2] "element_text" "element"
 - attr(*, "class")= chr [1:2] "theme" "gg"
 - attr(*, "complete")= logi TRUE
 - attr(*, "validate")= logi TRUE
# get lm for data
height_lm <- lm(CHS ~ 0 + YRI + CEU, data = al_freq_df$hei)

graph_reso <- 0.05

# set up axes
axis_x <- seq(min(al_freq_df$hei$YRI), max(al_freq_df$hei$YRI), by = graph_reso)
axis_y <- seq(min(al_freq_df$hei$CEU), max(al_freq_df$hei$CEU), by = graph_reso)

# sample points
height_lm_surface <- expand.grid(YRI = axis_x,
                                 CEU = axis_y,
                                 KEEP.OUT.ATTRS = F)
height_lm_surface$CHS <- predict.lm(height_lm, newdata = height_lm_surface)
height_lm_surface_cast <- reshape2::acast(height_lm_surface, CEU ~ YRI, value.var = "CHS")

Plot height

With assistance from here: https://stackoverflow.com/questions/38331198/add-regression-plane-to-3d-scatter-plot-in-plotly.

# plot
height_plot <- plot_ly(al_freq_df$hei,
                       x = ~CEU,
                       y = ~YRI,
                       z = ~CHS,
                       type = "scatter3d",
                       mode = "markers",
                       marker = list(size = 2))
# add surface
height_plot <- add_trace(p = height_plot,
                         z = height_lm_surface,
                         x = axis_x,
                         y = axis_y,
                         type = "surface")

height_plot
'surface' objects don't have these attributes: 'mode', 'marker'
Valid attributes include:
'type', 'visible', 'legendgroup', 'name', 'uid', 'ids', 'customdata', 'meta', 'hoverlabel', 'stream', 'uirevision', 'z', 'x', 'y', 'text', 'hovertext', 'hovertemplate', 'connectgaps', 'surfacecolor', 'cauto', 'cmin', 'cmax', 'cmid', 'colorscale', 'autocolorscale', 'reversescale', 'showscale', 'colorbar', 'coloraxis', 'contours', 'hidesurface', 'lightposition', 'lighting', 'opacity', '_deprecated', 'hoverinfo', 'showlegend', 'xcalendar', 'ycalendar', 'zcalendar', 'scene', 'idssrc', 'customdatasrc', 'metasrc', 'zsrc', 'xsrc', 'ysrc', 'textsrc', 'hovertextsrc', 'hovertemplatesrc', 'surfacecolorsrc', 'hoverinfosrc', 'key', 'set', 'frame', 'transforms', '_isNestedKey', '_isSimpleKey', '_isGraticule', '_bbox'
'surface' objects don't have these attributes: 'mode', 'marker'
Valid attributes include:
'type', 'visible', 'legendgroup', 'name', 'uid', 'ids', 'customdata', 'meta', 'hoverlabel', 'stream', 'uirevision', 'z', 'x', 'y', 'text', 'hovertext', 'hovertemplate', 'connectgaps', 'surfacecolor', 'cauto', 'cmin', 'cmax', 'cmid', 'colorscale', 'autocolorscale', 'reversescale', 'showscale', 'colorbar', 'coloraxis', 'contours', 'hidesurface', 'lightposition', 'lighting', 'opacity', '_deprecated', 'hoverinfo', 'showlegend', 'xcalendar', 'ycalendar', 'zcalendar', 'scene', 'idssrc', 'customdatasrc', 'metasrc', 'zsrc', 'xsrc', 'ysrc', 'textsrc', 'hovertextsrc', 'hovertemplatesrc', 'surfacecolorsrc', 'hoverinfosrc', 'key', 'set', 'frame', 'transforms', '_isNestedKey', '_isSimpleKey', '_isGraticule', '_bbox'
# get lm for data
height_loess <- loess(CHS ~ 0 + YRI + CEU, data = al_freq_df$hei)

graph_reso <- 0.05

# set up axes
axis_x <- seq(min(al_freq_df$hei$YRI), max(al_freq_df$hei$YRI), by = graph_reso)
axis_y <- seq(min(al_freq_df$hei$CEU), max(al_freq_df$hei$CEU), by = graph_reso)

# sample points
height_lm_surface <- expand.grid(YRI = axis_x,
                                 CEU = axis_y,
                                 KEEP.OUT.ATTRS = F)
height_lm_surface$CHS <- predict(height_loess, newdata = height_lm_surface)
height_lm_surface <- acast(height_lm_surface, CEU ~ YRI, value.var = "CHS")
# plot
height_plot <- plot_ly(al_freq_df$hei,
                       x = ~CEU,
                       y = ~YRI,
                       z = ~CHS,
                       type = "scatter3d",
                       mode = "markers",
                       marker = list(size = 2))
# add surface
height_plot <- add_trace(p = height_plot,
                         z = height_lm_surface,
                         x = axis_x,
                         y = axis_y,
                         type = "surface")

height_plot
'surface' objects don't have these attributes: 'mode', 'marker'
Valid attributes include:
'type', 'visible', 'legendgroup', 'name', 'uid', 'ids', 'customdata', 'meta', 'hoverlabel', 'stream', 'uirevision', 'z', 'x', 'y', 'text', 'hovertext', 'hovertemplate', 'connectgaps', 'surfacecolor', 'cauto', 'cmin', 'cmax', 'cmid', 'colorscale', 'autocolorscale', 'reversescale', 'showscale', 'colorbar', 'coloraxis', 'contours', 'hidesurface', 'lightposition', 'lighting', 'opacity', '_deprecated', 'hoverinfo', 'showlegend', 'xcalendar', 'ycalendar', 'zcalendar', 'scene', 'idssrc', 'customdatasrc', 'metasrc', 'zsrc', 'xsrc', 'ysrc', 'textsrc', 'hovertextsrc', 'hovertemplatesrc', 'surfacecolorsrc', 'hoverinfosrc', 'key', 'set', 'frame', 'transforms', '_isNestedKey', '_isSimpleKey', '_isGraticule', '_bbox'
'surface' objects don't have these attributes: 'mode', 'marker'
Valid attributes include:
'type', 'visible', 'legendgroup', 'name', 'uid', 'ids', 'customdata', 'meta', 'hoverlabel', 'stream', 'uirevision', 'z', 'x', 'y', 'text', 'hovertext', 'hovertemplate', 'connectgaps', 'surfacecolor', 'cauto', 'cmin', 'cmax', 'cmid', 'colorscale', 'autocolorscale', 'reversescale', 'showscale', 'colorbar', 'coloraxis', 'contours', 'hidesurface', 'lightposition', 'lighting', 'opacity', '_deprecated', 'hoverinfo', 'showlegend', 'xcalendar', 'ycalendar', 'zcalendar', 'scene', 'idssrc', 'customdatasrc', 'metasrc', 'zsrc', 'xsrc', 'ysrc', 'textsrc', 'hovertextsrc', 'hovertemplatesrc', 'surfacecolorsrc', 'hoverinfosrc', 'key', 'set', 'frame', 'transforms', '_isNestedKey', '_isSimpleKey', '_isGraticule', '_bbox'

Do again with MAF shuffled data

# get lm for data
height_loess <- loess(CHS ~ 0 + YRI + CEU, data = al_freq_df_shuff$hei)

graph_reso <- 0.05

# set up axes
axis_x <- seq(min(al_freq_df_shuff$hei$YRI), max(al_freq_df_shuff$hei$YRI), by = graph_reso)
axis_y <- seq(min(al_freq_df_shuff$hei$CEU), max(al_freq_df_shuff$hei$CEU), by = graph_reso)

# sample points
height_lm_surface <- expand.grid(YRI = axis_x,
                                 CEU = axis_y,
                                 KEEP.OUT.ATTRS = F)
height_lm_surface$CHS <- predict(height_loess, newdata = height_lm_surface)
height_lm_surface <- acast(height_lm_surface, CEU ~ YRI, value.var = "CHS")
# plot
height_plot <- plot_ly(al_freq_df_shuff$hei,
                       x = ~CEU,
                       y = ~YRI,
                       z = ~CHS,
                       type = "scatter3d",
                       mode = "markers",
                       marker = list(size = 2))
# add surface
height_plot <- add_trace(p = height_plot,
                         z = height_lm_surface,
                         x = axis_x,
                         y = axis_y,
                         type = "surface")

height_plot
'surface' objects don't have these attributes: 'mode', 'marker'
Valid attributes include:
'type', 'visible', 'legendgroup', 'name', 'uid', 'ids', 'customdata', 'meta', 'hoverlabel', 'stream', 'uirevision', 'z', 'x', 'y', 'text', 'hovertext', 'hovertemplate', 'connectgaps', 'surfacecolor', 'cauto', 'cmin', 'cmax', 'cmid', 'colorscale', 'autocolorscale', 'reversescale', 'showscale', 'colorbar', 'coloraxis', 'contours', 'hidesurface', 'lightposition', 'lighting', 'opacity', '_deprecated', 'hoverinfo', 'showlegend', 'xcalendar', 'ycalendar', 'zcalendar', 'scene', 'idssrc', 'customdatasrc', 'metasrc', 'zsrc', 'xsrc', 'ysrc', 'textsrc', 'hovertextsrc', 'hovertemplatesrc', 'surfacecolorsrc', 'hoverinfosrc', 'key', 'set', 'frame', 'transforms', '_isNestedKey', '_isSimpleKey', '_isGraticule', '_bbox'
'surface' objects don't have these attributes: 'mode', 'marker'
Valid attributes include:
'type', 'visible', 'legendgroup', 'name', 'uid', 'ids', 'customdata', 'meta', 'hoverlabel', 'stream', 'uirevision', 'z', 'x', 'y', 'text', 'hovertext', 'hovertemplate', 'connectgaps', 'surfacecolor', 'cauto', 'cmin', 'cmax', 'cmid', 'colorscale', 'autocolorscale', 'reversescale', 'showscale', 'colorbar', 'coloraxis', 'contours', 'hidesurface', 'lightposition', 'lighting', 'opacity', '_deprecated', 'hoverinfo', 'showlegend', 'xcalendar', 'ycalendar', 'zcalendar', 'scene', 'idssrc', 'customdatasrc', 'metasrc', 'zsrc', 'xsrc', 'ysrc', 'textsrc', 'hovertextsrc', 'hovertemplatesrc', 'surfacecolorsrc', 'hoverinfosrc', 'key', 'set', 'frame', 'transforms', '_isNestedKey', '_isSimpleKey', '_isGraticule', '_bbox'

Do again with MAF shuffled data

# get lm for data
height_loess <- loess(YRI ~ 0 + CEU + CHS, data = al_freq_df_shuff$hei)

graph_reso <- 0.05

# set up axes
axis_x <- seq(min(al_freq_df_shuff$hei$CEU), max(al_freq_df_shuff$hei$CEU), by = graph_reso)
axis_y <- seq(min(al_freq_df_shuff$hei$CHS), max(al_freq_df_shuff$hei$CHS), by = graph_reso)

# sample points
height_lm_surface <- expand.grid(CEU = axis_x,
                                 CHS = axis_y,
                                 KEEP.OUT.ATTRS = F)
height_lm_surface$YRI <- predict(height_loess, newdata = height_lm_surface)
height_lm_surface <- acast(height_lm_surface, CHS ~ CEU, value.var = "YRI")
# plot
height_plot <- plot_ly(al_freq_df_shuff$hei,
                       x = ~CEU,
                       y = ~CHS,
                       z = ~YRI,
                       type = "scatter3d",
                       mode = "markers",
                       marker = list(size = 2))
# add surface
height_plot <- add_trace(p = height_plot,
                         z = height_lm_surface,
                         x = axis_x,
                         y = axis_y,
                         type = "surface")

height_plot
'surface' objects don't have these attributes: 'mode', 'marker'
Valid attributes include:
'type', 'visible', 'legendgroup', 'name', 'uid', 'ids', 'customdata', 'meta', 'hoverlabel', 'stream', 'uirevision', 'z', 'x', 'y', 'text', 'hovertext', 'hovertemplate', 'connectgaps', 'surfacecolor', 'cauto', 'cmin', 'cmax', 'cmid', 'colorscale', 'autocolorscale', 'reversescale', 'showscale', 'colorbar', 'coloraxis', 'contours', 'hidesurface', 'lightposition', 'lighting', 'opacity', '_deprecated', 'hoverinfo', 'showlegend', 'xcalendar', 'ycalendar', 'zcalendar', 'scene', 'idssrc', 'customdatasrc', 'metasrc', 'zsrc', 'xsrc', 'ysrc', 'textsrc', 'hovertextsrc', 'hovertemplatesrc', 'surfacecolorsrc', 'hoverinfosrc', 'key', 'set', 'frame', 'transforms', '_isNestedKey', '_isSimpleKey', '_isGraticule', '_bbox'
'surface' objects don't have these attributes: 'mode', 'marker'
Valid attributes include:
'type', 'visible', 'legendgroup', 'name', 'uid', 'ids', 'customdata', 'meta', 'hoverlabel', 'stream', 'uirevision', 'z', 'x', 'y', 'text', 'hovertext', 'hovertemplate', 'connectgaps', 'surfacecolor', 'cauto', 'cmin', 'cmax', 'cmid', 'colorscale', 'autocolorscale', 'reversescale', 'showscale', 'colorbar', 'coloraxis', 'contours', 'hidesurface', 'lightposition', 'lighting', 'opacity', '_deprecated', 'hoverinfo', 'showlegend', 'xcalendar', 'ycalendar', 'zcalendar', 'scene', 'idssrc', 'customdatasrc', 'metasrc', 'zsrc', 'xsrc', 'ysrc', 'textsrc', 'hovertextsrc', 'hovertemplatesrc', 'surfacecolorsrc', 'hoverinfosrc', 'key', 'set', 'frame', 'transforms', '_isNestedKey', '_isSimpleKey', '_isGraticule', '_bbox'

Do for all

lapply(al_freq_df_shuff, function(pheno){
  # set graph resolution
  graph_reso <- 0.05
  # get lm for data
  loess_model <- loess(YRI ~ 0 + CEU + CHS, data = pheno)
  # set up axes
  axis_x <- seq(min(pheno$CEU), max(pheno$CEU), by = graph_reso)
  axis_y <- seq(min(pheno$CHS), max(pheno$CHS), by = graph_reso)
  # sample points
  lm_surface <- expand.grid(CEU = axis_x,
                            CHS = axis_y,
                            KEEP.OUT.ATTRS = F)
  lm_surface$YRI <- predict(loess_model, newdata = lm_surface)
  lm_surface <- acast(lm_surface, CHS ~ CEU, value.var = "YRI")
  # create plot
  plt <- plot_ly(pheno,
                 x = ~CEU,
                 y = ~CHS,
                 z = ~YRI,
                 type = "scatter3d",
                 mode = "markers",
                 marker = list(size = 2),
                 text = pheno$ID)
  # add surface
  plt <- add_trace(p = plt,
                   z = lm_surface,
                   x = axis_x,
                   y = axis_y,
                   type = "surface")
  
  plt  
})
$edu
'surface' objects don't have these attributes: 'mode', 'marker'
Valid attributes include:
'type', 'visible', 'legendgroup', 'name', 'uid', 'ids', 'customdata', 'meta', 'hoverlabel', 'stream', 'uirevision', 'z', 'x', 'y', 'text', 'hovertext', 'hovertemplate', 'connectgaps', 'surfacecolor', 'cauto', 'cmin', 'cmax', 'cmid', 'colorscale', 'autocolorscale', 'reversescale', 'showscale', 'colorbar', 'coloraxis', 'contours', 'hidesurface', 'lightposition', 'lighting', 'opacity', '_deprecated', 'hoverinfo', 'showlegend', 'xcalendar', 'ycalendar', 'zcalendar', 'scene', 'idssrc', 'customdatasrc', 'metasrc', 'zsrc', 'xsrc', 'ysrc', 'textsrc', 'hovertextsrc', 'hovertemplatesrc', 'surfacecolorsrc', 'hoverinfosrc', 'key', 'set', 'frame', 'transforms', '_isNestedKey', '_isSimpleKey', '_isGraticule', '_bbox'
'surface' objects don't have these attributes: 'mode', 'marker'
Valid attributes include:
'type', 'visible', 'legendgroup', 'name', 'uid', 'ids', 'customdata', 'meta', 'hoverlabel', 'stream', 'uirevision', 'z', 'x', 'y', 'text', 'hovertext', 'hovertemplate', 'connectgaps', 'surfacecolor', 'cauto', 'cmin', 'cmax', 'cmid', 'colorscale', 'autocolorscale', 'reversescale', 'showscale', 'colorbar', 'coloraxis', 'contours', 'hidesurface', 'lightposition', 'lighting', 'opacity', '_deprecated', 'hoverinfo', 'showlegend', 'xcalendar', 'ycalendar', 'zcalendar', 'scene', 'idssrc', 'customdatasrc', 'metasrc', 'zsrc', 'xsrc', 'ysrc', 'textsrc', 'hovertextsrc', 'hovertemplatesrc', 'surfacecolorsrc', 'hoverinfosrc', 'key', 'set', 'frame', 'transforms', '_isNestedKey', '_isSimpleKey', '_isGraticule', '_bbox'


$hei
'surface' objects don't have these attributes: 'mode', 'marker'
Valid attributes include:
'type', 'visible', 'legendgroup', 'name', 'uid', 'ids', 'customdata', 'meta', 'hoverlabel', 'stream', 'uirevision', 'z', 'x', 'y', 'text', 'hovertext', 'hovertemplate', 'connectgaps', 'surfacecolor', 'cauto', 'cmin', 'cmax', 'cmid', 'colorscale', 'autocolorscale', 'reversescale', 'showscale', 'colorbar', 'coloraxis', 'contours', 'hidesurface', 'lightposition', 'lighting', 'opacity', '_deprecated', 'hoverinfo', 'showlegend', 'xcalendar', 'ycalendar', 'zcalendar', 'scene', 'idssrc', 'customdatasrc', 'metasrc', 'zsrc', 'xsrc', 'ysrc', 'textsrc', 'hovertextsrc', 'hovertemplatesrc', 'surfacecolorsrc', 'hoverinfosrc', 'key', 'set', 'frame', 'transforms', '_isNestedKey', '_isSimpleKey', '_isGraticule', '_bbox'
'surface' objects don't have these attributes: 'mode', 'marker'
Valid attributes include:
'type', 'visible', 'legendgroup', 'name', 'uid', 'ids', 'customdata', 'meta', 'hoverlabel', 'stream', 'uirevision', 'z', 'x', 'y', 'text', 'hovertext', 'hovertemplate', 'connectgaps', 'surfacecolor', 'cauto', 'cmin', 'cmax', 'cmid', 'colorscale', 'autocolorscale', 'reversescale', 'showscale', 'colorbar', 'coloraxis', 'contours', 'hidesurface', 'lightposition', 'lighting', 'opacity', '_deprecated', 'hoverinfo', 'showlegend', 'xcalendar', 'ycalendar', 'zcalendar', 'scene', 'idssrc', 'customdatasrc', 'metasrc', 'zsrc', 'xsrc', 'ysrc', 'textsrc', 'hovertextsrc', 'hovertemplatesrc', 'surfacecolorsrc', 'hoverinfosrc', 'key', 'set', 'frame', 'transforms', '_isNestedKey', '_isSimpleKey', '_isGraticule', '_bbox'


$pig
'surface' objects don't have these attributes: 'mode', 'marker'
Valid attributes include:
'type', 'visible', 'legendgroup', 'name', 'uid', 'ids', 'customdata', 'meta', 'hoverlabel', 'stream', 'uirevision', 'z', 'x', 'y', 'text', 'hovertext', 'hovertemplate', 'connectgaps', 'surfacecolor', 'cauto', 'cmin', 'cmax', 'cmid', 'colorscale', 'autocolorscale', 'reversescale', 'showscale', 'colorbar', 'coloraxis', 'contours', 'hidesurface', 'lightposition', 'lighting', 'opacity', '_deprecated', 'hoverinfo', 'showlegend', 'xcalendar', 'ycalendar', 'zcalendar', 'scene', 'idssrc', 'customdatasrc', 'metasrc', 'zsrc', 'xsrc', 'ysrc', 'textsrc', 'hovertextsrc', 'hovertemplatesrc', 'surfacecolorsrc', 'hoverinfosrc', 'key', 'set', 'frame', 'transforms', '_isNestedKey', '_isSimpleKey', '_isGraticule', '_bbox'
'surface' objects don't have these attributes: 'mode', 'marker'
Valid attributes include:
'type', 'visible', 'legendgroup', 'name', 'uid', 'ids', 'customdata', 'meta', 'hoverlabel', 'stream', 'uirevision', 'z', 'x', 'y', 'text', 'hovertext', 'hovertemplate', 'connectgaps', 'surfacecolor', 'cauto', 'cmin', 'cmax', 'cmid', 'colorscale', 'autocolorscale', 'reversescale', 'showscale', 'colorbar', 'coloraxis', 'contours', 'hidesurface', 'lightposition', 'lighting', 'opacity', '_deprecated', 'hoverinfo', 'showlegend', 'xcalendar', 'ycalendar', 'zcalendar', 'scene', 'idssrc', 'customdatasrc', 'metasrc', 'zsrc', 'xsrc', 'ysrc', 'textsrc', 'hovertextsrc', 'hovertemplatesrc', 'surfacecolorsrc', 'hoverinfosrc', 'key', 'set', 'frame', 'transforms', '_isNestedKey', '_isSimpleKey', '_isGraticule', '_bbox'

NA

Try again with different order of variables - predicting YRI maybe not a good idea given level of variation

colourscales <- c("Viridis", "Hot", "Electric")
titles <- c("Educational Attainment", "Height", "Skin/hair pigmentation")

counter <- 0
lapply(al_freq_df_shuff, function(pheno){
  counter <<- counter + 1
  # set graph resolution
  graph_reso <- 0.05
  # get lm for data
  loess_model <- loess(CEU ~ 0 + CHS + YRI, data = pheno)
  # set up axes
  axis_x <- seq(min(pheno$CHS), max(pheno$CHS), by = graph_reso)
  axis_y <- seq(min(pheno$YRI), max(pheno$YRI), by = graph_reso)
  # sample points
  lm_surface <- expand.grid(CHS = axis_x,
                            YRI = axis_y,
                            KEEP.OUT.ATTRS = F)
  lm_surface$CEU <- predict(loess_model, newdata = lm_surface)
  lm_surface <- acast(lm_surface, YRI ~ CHS, value.var = "CEU")
  # create plot
  plt <- plot_ly(pheno,
                 x = ~CHS,
                 y = ~YRI,
                 z = ~CEU,
                 type = "scatter3d",
                 mode = "markers",
                 marker = list(size = 2),
                 text = pheno$ID) 
  plt <- add_trace(plt,
                   z = lm_surface,
              x = axis_x,
              y = axis_y,
              type = "surface",
              colorscale = colourscales[counter]) %>% 
    layout(title = titles[counter])
  plt
})
$edu


$hei


$pig

NA
LS0tCnRpdGxlOiAiUmFjaXN0IGh5cG90aGVzaXMgbm90ZWJvb2siCm91dHB1dDogaHRtbF9ub3RlYm9vawplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KCiMgU291cmNlIHBhY2thZ2VzIGFuZCBmdW5jdGlvbnMKCmBgYHtyfQpzb3VyY2UoaGVyZTo6aGVyZSgiY29kZSIsICJzY3JpcHRzIiwgIjIwMjAwMzE4X25vdGVib29rX3NvdXJjZS5SIikpCmBgYAoKKjIwMjAwMTMwKgoKIyBEYXRhCgrigKIgMUtHIGZpbmFsIHJlbGVhc2UgVkNGczogPGZ0cDovL2Z0cC4xMDAwZ2Vub21lcy5lYmkuYWMudWsvdm9sMS9mdHAvcmVsZWFzZS8yMDEzMDUwMi8+CuKAoiBFZHVjYXRpb25hbCBhdHRhaW5tZW50IHBhcGVyOiA8aHR0cHM6Ly93d3cubmF0dXJlLmNvbS9hcnRpY2xlcy9zNDE1ODgtMDE4LTAxNDctMz4K4oCiIExlYWQgbG9jaSBmcm9tIHRoYXQgcGFwZXI6IGByYWNpc3RfaHlwb3RoZXNpcy9kYXRhLzIwMjAwMTMwX2lxX2d3YXNfdG9wZi5jc3ZgCgpQYXBlcnM6CgrigKIgQmVyZyBKLiBKLiwgQ29vcCBHLiwgMjAxNCAnQSBwb3B1bGF0aW9uIGdlbmV0aWMgc2lnbmFsIG9mIHBvbHlnZW5pYyBhZGFwdGF0aW9uJy4gUExvUyBHZW5ldGljcyAxMDogZTEwMDQ0MTIgCuKAoiBBbHNvIHRoZWlyIDIwMTcgcGFwZXIgPGh0dHBzOi8vd3d3LmJpb3J4aXYub3JnL2NvbnRlbnQvMTAuMTEwMS8xNjc1NTF2ND4K4oCiIFNwaWVkZWwgZXQgYWwgb24gYSB0b29sIGZvciBpbmZlcnJpbmcgZ2VuZWFsb2d5IChhbmQgaGVuY2UgY2hhbmdlcyBpbiBhbGxlbGUgZnJlcXVlbmNpZXMsIGhlbmNlIHNlbGVjdGlvbik6IDxodHRwczovL3d3dy5uYXR1cmUuY29tL2FydGljbGVzL3M0MTU4OC0wMTktMDQ4NC14PgoKIyBDbHVzdGVyIHN0cnVjdHVyZQoKSG9tZTogYC9ocHMvcmVzZWFyY2gxL2Jpcm5leS91c2Vycy9pYW4vcmFjX2h5cGAKCmBgYHtiYXNofQpta2NkIHZjZnMKCiMgZG93bmxvYWQgVkNGIGFuZCBpbmRleAp3Z2V0IGZ0cDovL2Z0cC4xMDAwZ2Vub21lcy5lYmkuYWMudWsvdm9sMS9mdHAvcmVsZWFzZS8yMDEzMDUwMi9BTEwud2dzLnBoYXNlM19zaGFwZWl0Ml9tdm5jYWxsX2ludGVncmF0ZWRfdjViLjIwMTMwNTAyLnNpdGVzLnZjZi5negp3Z2V0IGZ0cDovL2Z0cC4xMDAwZ2Vub21lcy5lYmkuYWMudWsvdm9sMS9mdHAvcmVsZWFzZS8yMDEzMDUwMi9BTEwud2dzLnBoYXNlM19zaGFwZWl0Ml9tdm5jYWxsX2ludGVncmF0ZWRfdjViLjIwMTMwNTAyLnNpdGVzLnZjZi5nei50YmkKYGBgCgojIyBVcGRhdGUgR0FUSwoKYGBge2Jhc2h9CmNkIC9uZnMvc29mdHdhcmUvYmlybmV5CndnZXQgaHR0cHM6Ly9naXRodWIuY29tL2Jyb2FkaW5zdGl0dXRlL2dhdGsvcmVsZWFzZXMvZG93bmxvYWQvNC4xLjQuMS9nYXRrLTQuMS40LjEuemlwCnVuemlwIGdhdGstNC4xLjQuMS56aXAKCiMgYW1lbmQgYWxpYXNlcyBpbiB+Ly5iYXNocmMgYW5kIH4vLmJhc2hfcHJvZmlsZQpleHBvcnQgUEFUSD0kUEFUSDovbmZzL3NvZnR3YXJlL2Jpcm5leS9nYXRrLTQuMS40LjEvCmBgYAoKIyMgU2V0IHVwIHJlZmVyZW5jZQpgYGB7YmFzaH0KbWtjZCByZWZzCiMgZG93bmxvYWQgRkFTVEEKd2dldCBmdHA6Ly9mdHAuZW5zZW1ibC5vcmcvcHViL3JlbGVhc2UtOTkvZmFzdGEvaG9tb19zYXBpZW5zL2RuYS9Ib21vX3NhcGllbnMuR1JDaDM4LmRuYS50b3BsZXZlbC5mYS5negojIGNyZWF0ZSBkaWN0aW9uYXJ5IGZvbGx3b2luZyBndWlhZGFuY2UgaGVyZTogPGh0dHBzOi8vZ2F0a2ZvcnVtcy5icm9hZGluc3RpdHV0ZS5vcmcvZ2F0ay9kaXNjdXNzaW9uLzE2MDEvaG93LWNhbi1pLXByZXBhcmUtYS1mYXN0YS1maWxlLXRvLXVzZS1hcy1yZWZlcmVuY2U+CmphdmEgLWphciAvbmZzL3NvZnR3YXJlL2Jpcm5leS9waWNhcmQtMi45LjAvcGljYXJkLmphciBDcmVhdGVTZXF1ZW5jZURpY3Rpb25hcnkgXAogIFI9cmVmcy9Ib21vX3NhcGllbnMuR1JDaDM4LmRuYS50b3BsZXZlbC5mYS5neiBcCiAgTz1yZWZzL0hvbW9fc2FwaWVucy5HUkNoMzguZG5hLnRvcGxldmVsLmRpY3QKIyBjcmVhdGUgZmFzdGEgaW5kZXggZmlsZQovbmZzL3NvZnR3YXJlL2Jpcm5leS9zYW10b29scy0xLjkvc2FtdG9vbHMgZmFpZHggcmVmcy9Ib21vX3NhcGllbnMuR1JDaDM4LmRuYS50b3BsZXZlbC5mYS5negojIFtFOjpmYWlfYnVpbGQzX2NvcmVdIENhbm5vdCBpbmRleCBmaWxlcyBjb21wcmVzc2VkIHdpdGggZ3ppcCwgcGxlYXNlIHVzZSBiZ3ppcAojIyB1bnppcCAKZ3VuemlwIHJlZnMvSG9tb19zYXBpZW5zLkdSQ2gzOC5kbmEudG9wbGV2ZWwuZmEuZ3oKIyMgY3JlYXRlIGRpY3Rpb25hcnkgYWdhaW4Kcm0gcmVmcy9Ib21vX3NhcGllbnMuR1JDaDM4LmRuYS50b3BsZXZlbC5kaWN0CmphdmEgLWphciAvbmZzL3NvZnR3YXJlL2Jpcm5leS9waWNhcmQtMi45LjAvcGljYXJkLmphciBDcmVhdGVTZXF1ZW5jZURpY3Rpb25hcnkgXAogIFI9cmVmcy9Ib21vX3NhcGllbnMuR1JDaDM4LmRuYS50b3BsZXZlbC5mYSBcCiAgTz1yZWZzL0hvbW9fc2FwaWVucy5HUkNoMzguZG5hLnRvcGxldmVsLmRpY3QKIyBjcmVhdGUgZ2FzdCBpbmRleCBmaWxlCi9uZnMvc29mdHdhcmUvYmlybmV5L3NhbXRvb2xzLTEuOS9zYW10b29scyBmYWlkeCByZWZzL0hvbW9fc2FwaWVucy5HUkNoMzguZG5hLnRvcGxldmVsLmZhCmBgYAoKIyMgQ3JlYXRlIGxpc3Qgb2YgU05QcyBmcm9tIHRvcCBoaXRzIHRhYmxlOiBgcmFjaXN0X2h5cG90aGVzaXMvZGF0YS8yMDIwMDIwNF9zbnBzLmxpc3RgCmBgYHtiYXNofQpjdXQgcmFjaXN0X2h5cG90aGVzaXMvZGF0YS8yMDIwMDIwNF9pcV9nd2FzX3RvcGYgLWY1IC1kIiwiIHwgc2VkICdzLyIvL2cnIHwgdGFpbCAtbisyID4gcmFjaXN0X2h5cG90aGVzaXMvZGF0YS8yMDIwMDIwNF9zbnBzLmxpc3QgCmBgYAoKIyBQdWxsIG91dCBTTlBzIGZyb20gVkNGIGJhc2VkIG9uIGxvY2kKCmBgYHtiYXNofQpnYXRrIFNlbGVjdFZhcmlhbnRzIFwKICAtUiByZWZzL0hvbW9fc2FwaWVucy5HUkNoMzguZG5hLnRvcGxldmVsLmZhIFwKICAtViB2Y2ZzL0FMTC53Z3MucGhhc2UzX3NoYXBlaXQyX212bmNhbGxfaW50ZWdyYXRlZF92NWIuMjAxMzA1MDIuc2l0ZXMudmNmLmd6IFwKICAtLWtlZXAtaWRzIHJhY2lzdF9oeXBvdGhlc2lzL2RhdGEvMjAyMDAyMDRfc25wcy5saXN0IFwKICAtTyB2Y2ZzL0FMTC5oaXRzLnZjZi5negojIEVycm9yIGluaXRpYWxpemluZyBmZWF0dXJlIHJlYWRlciBmb3IgcGF0aCB2Y2ZzL0FMTC53Z3MucGhhc2UzX3NoYXBlaXQyX212bmNhbGxfaW50ZWdyYXRlZF92NWIuMjAxMzA1MDIuc2l0ZXMudmNmLmd6CiMgQ2F1c2VkIGJ5OiBodHNqZGsudHJpYmJsZS5UcmliYmxlRXhjZXB0aW9uJE1hbGZvcm1lZEZlYXR1cmVGaWxlOiBVbmFibGUgdG8gcGFyc2UgaGVhZGVyIHdpdGggZXJyb3I6IEludmFsaWQgR1pJUCBoZWFkZXIsIGZvciBpbnB1dCBzb3VyY2U6IHZjZnMvQUxMLndncy5waGFzZTNfc2hhcGVpdDJfbXZuY2FsbF9pbnRlZ3JhdGVkX3Y1Yi4yMDEzMDUwMi5zaXRlcy52Y2YuZ3ogIApgYGAKClRoaXMgVkNGIGRvZXNuJ3QgaGF2ZSB0aGUgcGVyLXNhbXBsZSBjYWxscywgYnV0IGp1c3QgdGhlIGFsbGVsZSBmcmVxdWVuY2llcyBmb3IgZWFjaCBjb250aW5lbnRhbCBwb3B1bGF0aW9uLgoKSGF2ZSB0byBkb3dubG9hZCBhbGwgdGhlIFZDRnMgb24gdGhlIHBhZ2UsIHRoZW4gcHV0IHRoZW0gdG9nZXRoZXIuCgojIyBEb3dubG9hZAoKYGBge2Jhc2h9CndnZXQgLXIgLXAgLWsgLS1uby1wYXJlbnQgLWN1dC1kaXJzPTUgZnRwOi8vZnRwLjEwMDBnZW5vbWVzLmViaS5hYy51ay92b2wxL2Z0cC9yZWxlYXNlLzIwMTMwNTAyLwpgYGAKCiMjIFB1dCBsaXN0IG9mIGZpbGVzIGludG8gbGlzdAoKYGBge2Jhc2h9CmZpbmQgdmNmcy9mdHAuMTAwMGdlbm9tZXMuZWJpLmFjLnVrL0FMTC5jaHIqLnZjZi5neiA+IHJhY2lzdF9oeXBvdGhlc2lzL2RhdGEvMjAyMDAyMDVfdmNmcy5saXN0CmBgYAoKIyMgTWVyZ2UgVkNGcwoKYGBge2Jhc2h9CmphdmEgLWphciAvbmZzL3NvZnR3YXJlL2Jpcm5leS9waWNhcmQtMi45LjAvcGljYXJkLmphciBNZXJnZVZjZnMgXAogIEk9cmFjaXN0X2h5cG90aGVzaXMvZGF0YS8yMDIwMDIwNV92Y2ZzLmxpc3QgXAogIE89dmNmcy8xZ2tfYWxsLnZjZi5negojIEV4Y2VwdGlvbiBpbiB0aHJlYWQgIm1haW4iIGphdmEubGFuZy5JbGxlZ2FsQXJndW1lbnRFeGNlcHRpb246IFRoZSBjb250aWcgZW50cmllcyBpbiBpbnB1dCBmaWxlIC9ocHMvcmVzZWFyY2gxL2Jpcm5leS91c2Vycy9pYW4vcmFjX2h5cC92Y2ZzL2Z0cC4xMDAwZ2Vub21lcy5lYmkuYWMudWsvQUxMLmNock1ULnBoYXNlM19jYWxsbW9tLXYwXzQuMjAxMzA1MDIuZ2Vub3R5cGVzLnZjZi5neiBhcmUgbm90IGNvbXBhdGlibGUgd2l0aCB0aGUgb3RoZXJzLgoKIyBTbyByZW1vdmUgdGhhdCBvbmUgZnJvbSBsaXN0IGFib3ZlCnNlZCAtaSAnL01UL2QnIHJhY2lzdF9oeXBvdGhlc2lzL2RhdGEvMjAyMDAyMDVfdmNmcy5saXN0CgojIHJ1biBNZXJnZVZDRnMgYWdhaW4KamF2YSAtamFyIC9uZnMvc29mdHdhcmUvYmlybmV5L3BpY2FyZC0yLjkuMC9waWNhcmQuamFyIE1lcmdlVmNmcyBcCiAgST1yYWNpc3RfaHlwb3RoZXNpcy9kYXRhLzIwMjAwMjA1X3ZjZnMubGlzdCBcCiAgTz12Y2ZzLzFna19hbGwudmNmLmd6CiAgCiMgRXhjZXB0aW9uIGluIHRocmVhZCAibWFpbiIgamF2YS5sYW5nLklsbGVnYWxBcmd1bWVudEV4Y2VwdGlvbjogVGhlIGNvbnRpZyBlbnRyaWVzIGluIGlucHV0IGZpbGUgL2hwcy9yZXNlYXJjaDEvYmlybmV5L3VzZXJzL2lhbi9yYWNfaHlwL3ZjZnMvZnRwLjEwMDBnZW5vbWVzLmViaS5hYy51ay9BTEwuY2hyWS5waGFzZTNfaW50ZWdyYXRlZF92MmEuMjAxMzA1MDIuZ2Vub3R5cGVzLnZjZi5neiBhcmUgbm90IGNvbXBhdGlibGUgd2l0aCB0aGUgb3RoZXJzLgpzZWQgLWkgJy9jaHJZL2QnIHJhY2lzdF9oeXBvdGhlc2lzL2RhdGEvMjAyMDAyMDVfdmNmcy5saXN0CgojIHJ1biBNZXJnZVZDRnMgYWdhaW4KamF2YSAtamFyIC9uZnMvc29mdHdhcmUvYmlybmV5L3BpY2FyZC0yLjkuMC9waWNhcmQuamFyIE1lcmdlVmNmcyBcCiAgST1yYWNpc3RfaHlwb3RoZXNpcy9kYXRhLzIwMjAwMjA1X3ZjZnMubGlzdCBcCiAgTz12Y2ZzLzFna19hbGwudmNmLmd6CiMgV09SS1MKYGBgCgojIyBHZXQgcmVmZXJlbmNlIHVzZWQgZm9yIHRoaXMgY2FsbHNldApgYGB7YmFzaH0KIyBGaW5kIG91dCB3aGljaCByZWZlcmVuY2UgaXMgdXNlZCBieSB0aGUgY2FsbHNldApiY2Z0b29scyB2aWV3IHZjZnMvMWdrX2FsbC52Y2YuZ3ogfCBsZXNzCiMgZnRwOi8vZnRwLjEwMDBnZW5vbWVzLmViaS5hYy51ay8vdm9sMS9mdHAvdGVjaG5pY2FsL3JlZmVyZW5jZS9waGFzZTJfcmVmZXJlbmNlX2Fzc2VtYmx5X3NlcXVlbmNlL2hzMzdkNS5mYS5negoKIyBkb3dubG9hZCB0aGF0IHJlZmVyZW5jZQpjZCByZWZzCndnZXQgZnRwOi8vZnRwLjEwMDBnZW5vbWVzLmViaS5hYy51ay8vdm9sMS9mdHAvdGVjaG5pY2FsL3JlZmVyZW5jZS9waGFzZTJfcmVmZXJlbmNlX2Fzc2VtYmx5X3NlcXVlbmNlL2hzMzdkNS5mYS5negojIGNyZWF0ZSBpbmRleAovbmZzL3NvZnR3YXJlL2Jpcm5leS9zYW10b29scy0xLjkvc2FtdG9vbHMgZmFpZHggcmVmcy9oczM3ZDUuZmEuZ3oKIyBjcmVhdGUgZGljdGlvbmFyeQpqYXZhIC1qYXIgL25mcy9zb2Z0d2FyZS9iaXJuZXkvcGljYXJkLTIuOS4wL3BpY2FyZC5qYXIgQ3JlYXRlU2VxdWVuY2VEaWN0aW9uYXJ5IFwKICBSPXJlZnMvaHMzN2Q1LmZhLmd6IFwKICBPPXJlZnMvaHMzN2Q1LmRpY3QKYGBgCgojIyBQdWxsIG91dCBTTlBzCgpgYGB7YmFzaH0KZ2F0ayBTZWxlY3RWYXJpYW50cyBcCiAgLVIgcmVmcy9oczM3ZDUuZmEuZ3ogXAogIC1WIHZjZnMvMWdrX2FsbC52Y2YuZ3ogXAogIC0ta2VlcC1pZHMgcmFjaXN0X2h5cG90aGVzaXMvZGF0YS8yMDIwMDIwNF9zbnBzLmxpc3QgXAogIC1PIHZjZnMvc25wX2hpdHMudmNmLmd6CiMgU1VDQ0VTUwpgYGAKCiMjIENvcHkgdG8gcmVwbwoKYGBge2Jhc2h9CmNwIHZjZnMvc25wX2hpdHMudmNmLmd6IHJhY2lzdF9oeXBvdGhlc2lzL2RhdGEvMjAyMDAzMDNfc25wX2hpdHMudmNmLmd6CmNwIHZjZnMvc25wX2hpdHMudmNmLmd6LnRiaSByYWNpc3RfaHlwb3RoZXNpcy9kYXRhLzIwMjAwMzAzX3NucF9oaXRzLnZjZi5nei50YmkKYGBgCgojIyBJbXBvcnQgaW50byBSCgpgYGB7cn0Kc25wX2hpdHMgPC0gcGVnYXM6OnJlYWQudmNmKGhlcmU6OmhlcmUoImRhdGEiLCAiMjAyMDAzMDNfc25wX2hpdHMudmNmLmd6IikpCmBgYAoKIyMgSW1wb3J0IHNhbXBsZSBpbmZvCgpGcm9tIGhlcmU6IDxodHRwOi8vZnRwLjEwMDBnZW5vbWVzLmViaS5hYy51ay92b2wxL2Z0cC90ZWNobmljYWwvd29ya2luZy8yMDEzMDYwNl9zYW1wbGVfaW5mby8yMDEzMDYwNl9zYW1wbGVfaW5mby54bHN4PiAobGluayBlbWJlZGRlZCBpbiB0aGlzIHBhZ2U6IDx3d3cuaW50ZXJuYXRpb25hbGdlbm9tZS5vcmcvZGF0YT4pCgpgYGB7cn0KbWV0YSA8LSByZWFkX3hsc3goaGVyZTo6aGVyZSgiZGF0YSIsICIyMDEzMDYwNl9zYW1wbGVfaW5mby54bHN4IiksIHNoZWV0ID0gIlNhbXBsZSBJbmZvIikgJT4lIGRwbHlyOjpzZWxlY3QoU2FtcGxlLCBQb3B1bGF0aW9uLCBHZW5kZXIpCmBgYAoKIyMgQXR0YWNoIHBvcHVsYXRpb24gY29sdW1uIHRvIHNucF9oaXRzCgpgYGB7cn0KIyBjcmVhdGUgcG9wdWxhdGlvbiB2ZWN0b3IKcG9wdWxhdGlvbnMgPC0gdW5saXN0KGxhcHBseShyb3duYW1lcyhzbnBfaGl0cyksIGZ1bmN0aW9uKHNhbXBsZSl7CiAgbWV0YSRQb3B1bGF0aW9uW21ldGEkU2FtcGxlID09IHNhbXBsZV0KfSkpCgojIGFkZCB0byBzbnBfaGl0cwpzbnBfaGl0cyRwb3B1bGF0aW9uIDwtIHBvcHVsYXRpb25zCiMgcmVvcmRlcgpzbnBfaGl0cyA8LSBzbnBfaGl0cyAlPiUgZHBseXI6OnNlbGVjdChwb3B1bGF0aW9uLCBldmVyeXRoaW5nKCkpCiMgc3BsaXQgYnkgcG9wdWxhdGlvbgpzbnBfaGl0c19zcGxpdCA8LSBzcGxpdChzbnBfaGl0cywgZiA9IHNucF9oaXRzJHBvcHVsYXRpb24pCiMgcmVtb3ZlIHBvcHVsYXRpb24gY29sdW1ucwpzbnBfaGl0c19zcGxpdCA8LSBsYXBwbHkoc25wX2hpdHNfc3BsaXQsIGZ1bmN0aW9uKHgpewogIHgkcG9wdWxhdGlvbiA8LSBOVUxMCiAgcmV0dXJuKHgpCn0pCgojIGdldCBhbGxlbGUgY291bnRzCmFsbGVsZV9jb3VudHMgPC0gbGFwcGx5KHNucF9oaXRzX3NwbGl0LCBmdW5jdGlvbihwb3B1bGF0aW9uKXsKICBzdW1tYXJ5KHBvcHVsYXRpb24pCn0pCmBgYAoKIyMgSW1wb3J0IGxpc3Qgb2YgU05QcyB3aXRoIGFsbGVsZXMKCmBgYHtyfQpzbnBfYWxfbm9zIDwtIHJlYWQuZGVsaW0oIn4vRG9jdW1lbnRzL1JlcG9zaXRvcmllcy9yYWNpc3RfaHlwb3RoZXNpcy9kYXRhLzIwMjAwMzAzX3RvcF9zbnBzX3dpdGhfYWxsZWxlcy50eHQiLCBhcy5pcyA9IFQpCmBgYAoKIyMgQ3JlYXRlIGRhdGEgZnJhbWVzIHdpdGggYWxsZWxlIGZyZXF1ZW5jZXMKCmBgYHtyfQojIFRFU1QKdGVzdCA8LSBhbGxlbGVfY291bnRzW1siQUNCIl1dW1sicnM3OTU4MjcxNCJdXQp0ZXN0MiA8LSBkYXRhLmZyYW1lKCJhbGxlbGUiID0gbmFtZXModGVzdCRhbGxlbGUpLAogICAgICAgICAgICAgICAgICAgICJjb3VudCIgPSB0ZXN0JGFsbGVsZSkKdGVzdDIkYWxfZnJlcSA8LSB0ZXN0MiRjb3VudCAvIHN1bSh0ZXN0MiRjb3VudCkKel9zY29yZXMgPC0gZGF0YS5mcmFtZSgiYWxsZWxlIiA9IGMoc25wX2FsX25vcyRBbGxlbGUxW3NucF9hbF9ub3MkU05QID09ICJyczc5NTgyNzE0Il0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNucF9hbF9ub3MkQWxsZWxlMltzbnBfYWxfbm9zJFNOUCA9PSAicnM3OTU4MjcxNCJdKSwKICAgICAgICAgICAgICAgICAgICAgICAiel9zY29yZSIgPSBjKDAsIHNucF9hbF9ub3MkWi5TY29yZVtzbnBfYWxfbm9zJFNOUCA9PSAicnM3OTU4MjcxNCJdKSkKdGVzdDMgPC0gZHBseXI6OmxlZnRfam9pbih0ZXN0Miwgel9zY29yZXMsIGJ5ID0gImFsbGVsZSIpCgojIENyZWF0ZSBmdW5jdGlvbgpnZXRfYWxmcmVxX3RhYmxlIDwtIGZ1bmN0aW9uKHBvcHVsYXRpb24sIHNucF9lZmZlY3RzX2RmKXsKICBjb3VudGVyIDwtIDAKICBwb3B1bGF0aW9uIDwtIGxhcHBseShwb3B1bGF0aW9uLCBmdW5jdGlvbihzbnBfc3VtbWFyeSl7CiAgICAjIHNldCBjb3VudGVyIGFuZCBleHRyYWN0IFNOUCBJRAogICAgY291bnRlciA8PC0gY291bnRlciArIDEKICAgIHNucF9pZCA8LSBuYW1lcyhwb3B1bGF0aW9uKVtjb3VudGVyXQogICAgIyBjcmVhdGUgZmluYWwgREYKICAgIGZpbmFsX2RmIDwtIGRhdGEuZnJhbWUoImFsbGVsZSIgPSBuYW1lcyhzbnBfc3VtbWFyeSRhbGxlbGUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiY291bnQiID0gc25wX3N1bW1hcnkkYWxsZWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKICAgICMgZ2V0IGFsbGVsZSBmcmVxdWVuY3kKICAgIGZpbmFsX2RmJGFsX2ZyZXEgPC0gZmluYWxfZGYkY291bnQgLyBzdW0oZmluYWxfZGYkY291bnQpCiAgICAjIHB1bGwgb3V0IHotc2NvcmVzIGFuZCBwLXZhbHVlcyBmcm9tIHNucF9lZmZlY3RzX2RmCiAgICB6X3Njb3JlcyA8LSBkYXRhLmZyYW1lKCJhbGxlbGUiID0gYyhzbnBfZWZmZWN0c19kZiRBbGxlbGUxW3NucF9lZmZlY3RzX2RmJFNOUCA9PSBzbnBfaWRdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc25wX2VmZmVjdHNfZGYkQWxsZWxlMltzbnBfZWZmZWN0c19kZiRTTlAgPT0gc25wX2lkXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJ6X3Njb3JlIiA9IGMoMCwgc25wX2VmZmVjdHNfZGYkWi5TY29yZVtzbnBfZWZmZWN0c19kZiRTTlAgPT0gc25wX2lkXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwX3ZhbHVlIiA9IGMoMCwgc25wX2VmZmVjdHNfZGYkUFtzbnBfZWZmZWN0c19kZiRTTlAgPT0gc25wX2lkXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQogICAgIyBqb2luIERGcy4gTm90ZSB0aGF0IGl0IG9ubHkgam9pbnMgdGhlIHR3byBhbGxlbGVzIGluIHRoZSBzbnBfZWZmZWN0c19kZgogICAgZmluYWxfZGYgPC0gZHBseXI6OmxlZnRfam9pbih6X3Njb3JlcywgZmluYWxfZGYsIGJ5ID0gImFsbGVsZSIpCiAgICByZXR1cm4oZmluYWxfZGYpCiAgfSkKICByZXR1cm4ocG9wdWxhdGlvbikKfQoKIyBSdW4gb3ZlciBsaXN0CmFsY250X2RmX2xzdCA8LSBsYXBwbHkoYWxsZWxlX2NvdW50cywgZnVuY3Rpb24oeCl7CiAgb3V0IDwtIGdldF9hbGZyZXFfdGFibGUoeCwgc25wX2FsX25vcykKICBmaW5hbCA8LSBkcGx5cjo6YmluZF9yb3dzKG91dCwgLmlkID0gInJzaWQiKQogIHJldHVybihmaW5hbCkKfSkKCiMgY3JlYXRlIGZpbmFsIERGCmFsY250X2RmIDwtIGRwbHlyOjpiaW5kX3Jvd3MoYWxjbnRfZGZfbHN0LCAuaWQgPSAicG9wdWxhdGlvbiIpCgojIGZpbHRlciBvbmx5IGZvciBhbGxlbGVzIHdpdGggYW4gZWZmZmVjdAphbGNudF9kZl9maWx0IDwtIGFsY250X2RmW2FsY250X2RmJHpfc2NvcmUgIT0gMCwgXQoKIyBzZXQgbmV3IHJvd25hbWVzCnJvd25hbWVzKGFsY250X2RmX2ZpbHQpIDwtIHNlcSgxOm5yb3coYWxjbnRfZGZfZmlsdCkpCmBgYAoKIyBQbG90CgojIyBTZXBhcmF0ZSBjb2x1bW5zIHRvIHBsb3Qgb24gZWl0aGVyIGF4aXMKCmBgYHtyfQojIGZpbHRlciBmb3IganVzdCBhbF9mcmVxCnBsb3RfZGYgPC0gYWxjbnRfZGZfZmlsdCAlPiUgZHBseXI6OnNlbGVjdCgtY291bnQsICkKcGxvdF9kZiA8LSB0aWR5cjo6cGl2b3Rfd2lkZXIoZGF0YSA9IHBsb3RfZGYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWVzX2Zyb20gPSBwb3B1bGF0aW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lc19wcmVmaXggPSAiYWxfZnJlcV8iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXNfZnJvbSA9IGFsX2ZyZXEpCgpkcGx5cjo6ZmlsdGVyKHBsb3RfZGYsIHpfc2NvcmUgPD0gMCkKYGBgCgojIyBQbG90CgojIyMgWVJJIHYgQ0VVCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkcGx5cjo6ZmlsdGVyKHBsb3RfZGYsIHpfc2NvcmUgPD0gMCksIGFlcyhhbF9mcmVxX1lSSSwgYWxfZnJlcV9DRVUsIGNvbG91ciA9IHpfc2NvcmUpKSArCiAgZ2VvbV9wb2ludCgpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2MoKSArCiAgY29vcmRfZml4ZWQoKSArCiAgZ2VvbV9zbW9vdGgoc2UgPSBGKSArCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxKSArCiAgZ2d0aXRsZSgiSVEtbmVnYXRpdmUgYWxsZWxlcyIpCgpnZ3Bsb3QoZGF0YSA9IGRwbHlyOjpmaWx0ZXIocGxvdF9kZiwgel9zY29yZSA+PSAwKSwgYWVzKGFsX2ZyZXFfWVJJLCBhbF9mcmVxX0NFVSwgY29sb3VyID0gel9zY29yZSkpICsKICBnZW9tX3BvaW50KCkgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpICsKICBjb29yZF9maXhlZCgpICsKICBnZW9tX3Ntb290aChzZSA9IEYpICsKICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAwLCBzbG9wZSA9IDEpICsKICBnZ3RpdGxlKCJJUS1wb3NpdGl2ZSBhbGxlbGVzIikKYGBgCgojIyMgWVJJIHYgQ0hTCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkcGx5cjo6ZmlsdGVyKHBsb3RfZGYsIHpfc2NvcmUgPD0gMCksIGFlcyhhbF9mcmVxX1lSSSwgYWxfZnJlcV9DSFMsIGNvbG91ciA9IHpfc2NvcmUpKSArCiAgZ2VvbV9wb2ludCgpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2MoKSArCiAgY29vcmRfZml4ZWQoKSArCiAgZ2VvbV9zbW9vdGgoc2UgPSBGKSArCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxKSArCiAgZ2d0aXRsZSgiSVEtbmVnYXRpdmUgYWxsZWxlcyIpCgpnZ3Bsb3QoZGF0YSA9IGRwbHlyOjpmaWx0ZXIocGxvdF9kZiwgel9zY29yZSA+PSAwKSwgYWVzKGFsX2ZyZXFfWVJJLCBhbF9mcmVxX0NIUywgY29sb3VyID0gel9zY29yZSkpICsKICBnZW9tX3BvaW50KCkgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpICsKICBjb29yZF9maXhlZCgpICsKICBnZW9tX3Ntb290aChzZSA9IEYpICsKICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAwLCBzbG9wZSA9IDEpICsKICBnZ3RpdGxlKCJJUS1wb3NpdGl2ZSBhbGxlbGVzIikKYGBgCgojIyMgQ0VVIHYgQ0hTCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkcGx5cjo6ZmlsdGVyKHBsb3RfZGYsIHpfc2NvcmUgPD0gMCksIGFlcyhhbF9mcmVxX0NFVSwgYWxfZnJlcV9DSFMsIGNvbG91ciA9IHpfc2NvcmUpKSArCiAgZ2VvbV9wb2ludCgpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2MoKSArCiAgY29vcmRfZml4ZWQoKSArCiAgZ2VvbV9zbW9vdGgoc2UgPSBGKSArCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxKSArCiAgZ2d0aXRsZSgiSVEtbmVnYXRpdmUgYWxsZWxlcyIpCgpnZ3Bsb3QoZGF0YSA9IGRwbHlyOjpmaWx0ZXIocGxvdF9kZiwgel9zY29yZSA+PSAwKSwgYWVzKGFsX2ZyZXFfQ0VVLCBhbF9mcmVxX0NIUywgY29sb3VyID0gel9zY29yZSkpICsKICBnZW9tX3BvaW50KCkgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpICsKICBjb29yZF9maXhlZCgpICsKICBnZW9tX3Ntb290aChzZSA9IEYpICsKICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAwLCBzbG9wZSA9IDEpICsKICBnZ3RpdGxlKCJJUS1wb3NpdGl2ZSBhbGxlbGVzIikKYGBgCgojIyBSdW4gRnN0CgpgYGB7cn0KZnN0X291dCA8LSBhcy5kYXRhLmZyYW1lKHBlZ2FzOjpGc3Qoc25wX2hpdHMsIHBvcCA9IHBvcHVsYXRpb25zKSkKCiMgbWFrZSByb3duYW1lcyBpbnRvIHNlcGFyYXRlIGNvbHVtbgpmc3Rfb3V0JHJzaWQgPC0gcm93bmFtZXMoZnN0X291dCkKCiMgam9pbiB0byBzbnBfYWxfbm9zIERGCnRlc3QgPC0gZHBseXI6OmxlZnRfam9pbihzbnBfYWxfbm9zLAogICAgICAgICAgICAgICAgICAgICAgICAgZHBseXI6OnNlbGVjdChmc3Rfb3V0LCByc2lkLCBGc3QpLAogICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSBjKCJTTlAiID0gInJzaWQiKSkKCiMgcGxvdApnZ3Bsb3QodGVzdCwgYWVzKFNOUCwgRnN0KSkgKwogIGdlb21fcG9pbnQoKSArCiAgdGhlbWVfYncoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCkpCmBgYAoKIyMgSWRlbnRpZnkgU05QcyB3aXRoIEZzdCBhYm92ZSAwLjIKCmBgYHtyfQpoaWdoX2ZzdF9zbnBzIDwtIHRlc3QkU05QW3Rlc3QkRnN0ID4gMC4yXQpgYGAKCiMjIFBsb3QgYWJvdmUgd2l0aCBsYWJlbHMgZm9yIHRoZXNlIFNOUHMKCmBgYHtyfQpsaWJyYXJ5KGdncmVwZWwpCmdncGxvdChkYXRhID0gZHBseXI6OmZpbHRlcihwbG90X2RmLCB6X3Njb3JlIDw9IDApLCBhZXMoYWxfZnJlcV9ZUkksIGFsX2ZyZXFfQ0VVLCBjb2xvdXIgPSB6X3Njb3JlLCBsYWJlbCA9IHJzaWQpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX2xhYmVsX3JlcGVsKGFlcyhsYWJlbCA9IGlmZWxzZShyc2lkICVpbiUgaGlnaF9mc3Rfc25wcywgcnNpZCwgJycpLCApKSArCiAgI2dlb21fdGV4dChhZXMobGFiZWwgPSBpZmVsc2UocnNpZCAlaW4lIGhpZ2hfZnN0X3NucHMsIHJzaWQsICcnKSksIGhqdXN0ID0gMCwgdmp1c3QgPSAwLCBjb2xvdXIgPSAiYmxhY2siKSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19jKCkgKwogIGNvb3JkX2ZpeGVkKCkgKwogIGdlb21fc21vb3RoKHNlID0gRikgKwogIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDAsIHNsb3BlID0gMSkgKwogIGdndGl0bGUoIklRLW5lZ2F0aXZlIGFsbGVsZXMiKQoKZ2dwbG90KGRhdGEgPSBkcGx5cjo6ZmlsdGVyKHBsb3RfZGYsIHpfc2NvcmUgPj0gMCksIGFlcyhhbF9mcmVxX1lSSSwgYWxfZnJlcV9DRVUsIGNvbG91ciA9IHpfc2NvcmUpKSArCiAgZ2VvbV9wb2ludCgpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2MoKSArCiAgY29vcmRfZml4ZWQoKSArCiAgZ2VvbV9zbW9vdGgoc2UgPSBGKSArCiAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxKSArCiAgZ2d0aXRsZSgiSVEtcG9zaXRpdmUgYWxsZWxlcyIpCmBgYAoKIyBSdW4gYWdhaW4gb24gU05QcyBmcm9tICpMZWUgZXQgYWwgKDIwMTkpIEdlbmUgZGlzY292ZXJ5IGFuZCBwb2x5Z2VuaWMgcHJlZGljdGlvbiBmcm9tIGEgZ2Vub21lLXdpZGUgYXNzb2NpYXRpb24gc3R1ZHkgb2YgZWR1Y2F0aW9uYWwgYXR0YWlubWVudCBpbiAxLjEgbWlsbGlvbiBpbmRpdmlkdWFscyoKCiMjIFB1bGwgb3V0IFNOUHMgZnJvbSBwYXBlcgoKYGBge3J9CiMgZXh0cmFjdCBmcm9tIGV4Y2VsIGRvYwpzbnBzX2VkdXlycyA8LSByZWFkX3hsc3goIn4vRG9jdW1lbnRzL1JlcG9zaXRvcmllcy9yYWNpc3RfaHlwb3RoZXNpcy9kYXRhLzIwMTgwNzIzX0xlZS1ldC1hbF9zdXBwLXRhYmxlcy54bHN4Iiwgc2hlZXQgPSAiMi4gRWR1WWVhcnMgTGVhZCBTTlBzIiwgc2tpcCA9IDEsIG5fbWF4ID0gMTI3MSkKIyB3cml0ZSB0YWJsZSBvZiBTTlBzCndyaXRlLnRhYmxlKHNucHNfZWR1eXJzW1siU05QIl1dLCAifi9Eb2N1bWVudHMvUmVwb3NpdG9yaWVzL3JhY2lzdF9oeXBvdGhlc2lzL2RhdGEvMjAyMDAzMTZfc25wc19lZHV5ZWFycy5saXN0IiwgcXVvdGUgPSBGLCByb3cubmFtZXMgPSBGLCBjb2wubmFtZXMgPSBGKQpgYGAKCiMjIFNlbGVjdCB0aG9zZSBTTlBzIGZyb20gdGhlIFZDRgoKYGBge2Jhc2h9CmdhdGsgU2VsZWN0VmFyaWFudHMgXAogIC1SIHJlZnMvaHMzN2Q1LmZhLmd6IFwKICAtViB2Y2ZzLzFna19hbGwudmNmLmd6IFwKICAtLWtlZXAtaWRzIHJhY2lzdF9oeXBvdGhlc2lzL2RhdGEvMjAyMDAzMTZfc25wc19lZHV5ZWFycy5saXN0IFwKICAtTyB2Y2ZzL3NucGhpdHNfZWR1eXJzLnZjZi5negpgYGAKCiMgR2V0IG1vcmUgU05QcyBmb3IgaGVpZ2h0CgpGcm9tIHRoZSBHV0FTIGNhdGFsb2cgaGVyZTogPGh0dHBzOi8vd3d3LmViaS5hYy51ay9nd2FzL2Vmb3RyYWl0cy9FRk9fMDAwNDMzOT4uIAoKRG9uJ3QgaW5jbHVkZSBjaGlsZCB0cmFpdCBkYXRhLiBMZWF2ZXMgKjQ5MDcqIGFzc29jaWF0aW9ucy4KClNhdmVkIGhlcmU6IGB+L0RvY3VtZW50cy9SZXBvc2l0b3JpZXMvcmFjaXN0X2h5cG90aGVzaXMvZGF0YS8yMDIwMDMwNF9yYXdfYXNzb2NpYXRpb25zX2hlaWdodC5jc3ZgCgpgYGB7YmFzaH0KIyBwdWxsIG91dCByc0lEcwpjYXQgZGF0YS8yMDIwMDMwNF9yYXdfYXNzb2NpYXRpb25zX2hlaWdodC5jc3YgfCBjdXQgLWYxIC1kJy0nID4gdG1wX3JzaWQudHh0IAojIHB1bGwgb3V0IGxpbmVzIHdpdGggcmlzayBhbGxlbGVzIG9ubHkgCmdyZXAgIjxiPlx3PC9iPiIgZGF0YS8yMDIwMDMwNF9yYXdfYXNzb2NpYXRpb25zX2hlaWdodC5jc3YgPiBkYXRhLzIwMjAwMzE2X3Jhd19hc3NvY2lhdGlvbnNfaGVpZ2h0X2VkaXRlZC5jc3YKIyBwdWxsIG91dCByc0lEcyBhbmQgcGFzdGUgaW50byBlZGl0ZWQgZG9jCmN1dCAtZjEgLWQnLScgZGF0YS8yMDIwMDMxNl9yYXdfYXNzb2NpYXRpb25zX2hlaWdodF9lZGl0ZWQuY3N2CiMgbm90ZTogc3BsaXQgIlZhcmlhbnQgYW5kIHJpc2sgYWxsZWxlIiBjb2x1bW4gaW50byB0d28gbWFudWFsbHkKYGBgCgoqMjAyMDAzMTYqIAoKIyMgT2J0YWluIGRhdGEKCkEgYml0IG1lc3N5LiBUcnkgZ2V0dGluZyB0aGUgaGVpZ2h0IFNOUHMgZnJvbSB0aGlzIHBhcGVyIGluc3RlYWQ6CgpZZW5nbyBldCBhbC4gKDIwMTgpICpNZXRhLWFuYWx5c2lzIG9mIGdlbm9tZS13aWRlIGFzc29jaWF0aW9uIHN0dWRpZXMgZm9yIGhlaWdodCBhbmQgYm9keSBtYXNzIGluZGV4IGluIGFwcHJveGltYXRlbHkgNzAwMDAwIGluZGl2aWR1YWxzIG9mIEV1cm9wZWFuIGFuY2VzdHJ5KgoKRGF0YSBkb3dubG9hZGVkIGZyb20gaGVyZToKPGh0dHBzOi8vcG9ydGFscy5icm9hZGluc3RpdHV0ZS5vcmcvY29sbGFib3JhdGlvbi9naWFudC9pbmRleC5waHAvR0lBTlRfY29uc29ydGl1bV9kYXRhX2ZpbGVzPgpNb3JlIHNwZWNpZmljYWxseTogPGh0dHBzOi8vcG9ydGFscy5icm9hZGluc3RpdHV0ZS5vcmcvY29sbGFib3JhdGlvbi9naWFudC9pbWFnZXMvZS9lMi9NZXRhLWFuYWx5c2lzX0xvY2tlX2V0X2FsJTJCVUtCaW9iYW5rXzIwMThfdG9wXzk0MV9mcm9tX0NPSk9fYW5hbHlzaXNfVVBEQVRFRC50eHQuZ3o+IAoKYGBge2Jhc2h9CmNkIHJhY2lzdF9oeXBvdGhlc2lzL2RhdGEKIyBkb3dubG9hZAp3Z2V0IGh0dHBzOi8vcG9ydGFscy5icm9hZGluc3RpdHV0ZS5vcmcvY29sbGFib3JhdGlvbi9naWFudC9pbWFnZXMvZS9lMi9NZXRhLWFuYWx5c2lzX0xvY2tlX2V0X2FsJTJCVUtCaW9iYW5rXzIwMThfdG9wXzk0MV9mcm9tX0NPSk9fYW5hbHlzaXNfVVBEQVRFRC50eHQuZ3oKIyB1bnppcApndW56aXAgTWV0YS1hbmFseXNpc19Mb2NrZV9ldF9hbCUyQlVLQmlvYmFua18yMDE4X3RvcF85NDFfZnJvbV9DT0pPX2FuYWx5c2lzX1VQREFURUQudHh0Lmd6CiMgcmVuYW1lCm12IE1ldGEtYW5hbHlzaXNfV29vZF9ldF9hbCtVS0Jpb2JhbmtfMjAxOF90b3BfMzI5MF9mcm9tX0NPSk9fYW5hbHlzaXMudHh0IDIwMTgxMDE1X1llbmdvLWV0LWFsX3NucHNfaGVpZ2h0LnR4dAoKYGBgCgojIyBQdWxsIG91dCBsaXN0IG9mIFNOUHMKCmBgYHtiYXNofQpjdXQgLWYxIDIwMTgxMDE1X1llbmdvLWV0LWFsX3NucHNfaGVpZ2h0LnR4dCB8IHRhaWwgLW4rMiA+IDIwMjAwMzE4X3NucHNfaGVpZ2h0Lmxpc3QKYGBgCgojIyBFeHRyYWN0IGNhbGxzIGZvciB0aG9zZSBTTlBzIGZyb20gVkNGCgpgYGB7YmFzaH0KZ2F0ayBTZWxlY3RWYXJpYW50cyBcCiAgLVIgcmVmcy9oczM3ZDUuZmEuZ3ogXAogIC1WIHZjZnMvMWdrX2FsbC52Y2YuZ3ogXAogIC0ta2VlcC1pZHMgcmFjaXN0X2h5cG90aGVzaXMvZGF0YS8yMDIwMDMxOF9zbnBzX2hlaWdodC5saXN0IFwKICAtTyB2Y2ZzL3NucGhpdHNfaGVpZ2h0LnZjZi5negpgYGAKCioyMDIwMDMxOCoKCkdldCBTTlBzIGZvciBJQkQuCgoqSHVhbmcgZXQgYWwuICgyMDE3KSBGaW5lLW1hcHBpbmcgaW5mbGFtbWF0b3J5IGJvd2VsIGRpc2Vhc2UgbG9jaSB0byBzaW5nbGUtdmFyaWFudCByZXNvbHV0aW9uKgoKRGF0YSBkb3dubG9hZGVkIGhlcmU6IDxodHRwczovL3d3dy5uYXR1cmUuY29tL2FydGljbGVzL25hdHVyZTIyOTY5I1NlYzI5Pi4KTW9yZSBzcGVjaWZpY2FsbHksIFN1cHBsZW1lbnRhcnkgVGFibGUgMTogPGh0dHBzOi8vc3RhdGljLWNvbnRlbnQuc3ByaW5nZXIuY29tL2VzbS9hcnQlM0ExMC4xMDM4JTJGbmF0dXJlMjI5NjkvTWVkaWFPYmplY3RzLzQxNTg2XzIwMTdfQkZuYXR1cmUyMjk2OV9NT0VTTTJfRVNNLnhsc3g+ClNhdmVkIGhlcmU6IGBkYXRhLzIwMTcwNjI4X0h1YW5nLWV0LWFsX3N1cHAtdGFibGUtMS54bHN4YAoKIyMgUHVsbCBvdXQgU05QcyBmcm9tIHBhcGVyCgpgYGB7cn0KIyBleHRyYWN0IGZyb20gZXhjZWwgZG9jCnNucHNfaWJkIDwtIHJlYWRfeGxzeChwYXRoID0gaGVyZTo6aGVyZSgiZGF0YSIsICIyMDE3MDYyOF9IdWFuZy1ldC1hbF9zdXBwLXRhYmxlLTEueGxzeCIpLAogICAgICAgICAgICAgICAgICAgICAgc2hlZXQgPSAibGlzdCBvZiB2YXJpYW50cyIsICkKc25wc19pYmRfY3NsIDwtIHJlYWRfeGxzeChwYXRoID0gaGVyZTo6aGVyZSgiZGF0YSIsICIyMDE3MDYyOF9IdWFuZy1ldC1hbF9zdXBwLXRhYmxlLTEueGxzeCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgIHNoZWV0ID0gImxpc3Qgb2YgY3JlZGlibGUgc2V0cyIsICkKIyB3cml0ZSB0YWJsZXMgb2YgU05Qcwp3cml0ZS50YWJsZSh4ID0gc25wc19pYmQkdmFyaWFudCwKICAgICAgICAgICAgZmlsZSA9IGhlcmU6OmhlcmUoImRhdGEiLCAiMjAyMDAzMTlfc25wc19pYmQubGlzdCIpLAogICAgICAgICAgICBxdW90ZSA9IEYsCiAgICAgICAgICAgIHJvdy5uYW1lcyA9IEYsCiAgICAgICAgICAgIGNvbC5uYW1lcyA9IEYpCndyaXRlLnRhYmxlKHggPSBzbnBzX2liZF9jc2wkdmFyaWFudC5sZWFkLAogICAgICAgICAgICBmaWxlID0gaGVyZTo6aGVyZSgiZGF0YSIsICIyMDIwMDMxOV9zbnBzX2liZF9jc2wubGlzdCIpLAogICAgICAgICAgICBxdW90ZSA9IEYsCiAgICAgICAgICAgIHJvdy5uYW1lcyA9IEYsCiAgICAgICAgICAgIGNvbC5uYW1lcyA9IEYpCmBgYAoKIyMgR2V0IFZDRnMKCmBgYHtiYXNofQpnYXRrIFNlbGVjdFZhcmlhbnRzIFwKICAtUiByZWZzL2hzMzdkNS5mYS5neiBcCiAgLVYgdmNmcy8xZ2tfYWxsLnZjZi5neiBcCiAgLS1rZWVwLWlkcyByYWNpc3RfaHlwb3RoZXNpcy9kYXRhLzIwMjAwMzE5X3NucHNfaWJkX2NzbC5saXN0IFwKICAtTyB2Y2ZzL3NucGhpdHNfaWJkLnZjZi5negogIApnYXRrIFNlbGVjdFZhcmlhbnRzIFwKICAtUiByZWZzL2hzMzdkNS5mYS5neiBcCiAgLVYgdmNmcy8xZ2tfYWxsLnZjZi5neiBcCiAgLS1rZWVwLWlkcyByYWNpc3RfaHlwb3RoZXNpcy9kYXRhLzIwMjAwMzE5X3NucHNfaWJkLmxpc3QgXAogIC1PIHZjZnMvc25waGl0c19pYmRfZnVsbC52Y2YuZ3oKYGBgCgojIyBDb3B5IFZDRnMgZm9yIGFsbCBpbnRvIGRhdGEgZm9sZGVyCgpgYGB7YmFzaH0KY3AgdmNmcy9zbnBoaXRzKiByYWNpc3RfaHlwb3RoZXNpcy9kYXRhCmBgYAoKKjIwMjAwMzIwKgoKIyBBbmFseXNpcyBQcm9wZXIKCiMjIEltcG9ydCAxR0sgbWV0YWRhdGEgKGZvciBwb3B1bGF0aW9uKQoKRnJvbSBoZXJlOiA8aHR0cDovL2Z0cC4xMDAwZ2Vub21lcy5lYmkuYWMudWsvdm9sMS9mdHAvdGVjaG5pY2FsL3dvcmtpbmcvMjAxMzA2MDZfc2FtcGxlX2luZm8vMjAxMzA2MDZfc2FtcGxlX2luZm8ueGxzeD4gKGxpbmsgZW1iZWRkZWQgaW4gdGhpcyBwYWdlOiA8d3d3LmludGVybmF0aW9uYWxnZW5vbWUub3JnL2RhdGE+KQoKYGBge3J9Cm1ldGEgPC0gcmVhZF94bHN4KGhlcmU6OmhlcmUoImRhdGEiLCAiMjAxMzA2MDZfc2FtcGxlX2luZm8ueGxzeCIpLCBzaGVldCA9ICJTYW1wbGUgSW5mbyIpICU+JSBkcGx5cjo6c2VsZWN0KFNhbXBsZSwgUG9wdWxhdGlvbiwgR2VuZGVyKQpgYGAKCiMjIFJlYWQgaW4gVkNGcyB3aXRoIGFsbGVsZSBjb3VudHMgZm9yIHRhcmdldCBTTlBzCgpgYGB7cn0KIyBsaXN0IHRhcmdldCBWQ0ZzCnRhcmdldF92Y2ZzIDwtIGxpc3QuZmlsZXMoaGVyZTo6aGVyZSgiZGF0YSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSBnbG9iMnJ4KCJzbnBoaXRzXyouZ3oiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgZnVsbC5uYW1lcyA9IFQpCiMgcmVtb3ZlIHRoZSBzaG9ydGVyIElCRCBsaXN0CnRhcmdldF92Y2ZzIDwtIHRhcmdldF92Y2ZzWzE6M10KCiMgcmVhZCBpbiBWQ0ZzIGFuZCBnZXQgYWxsZWxlIGNvdW50cwp2Y2ZfbGlzdCA8LSBsYXBwbHkodGFyZ2V0X3ZjZnMsIGZ1bmN0aW9uKHZjZl9maWxlKXsKICAjIHJlYWQgaW4gVkNGcwogIHZjZl9vdXQgPC0gcGVnYXM6OnJlYWQudmNmKHZjZl9maWxlKQogICMgY3JlYXRlIHBvcHVsYXRpb24gY29sdW1uCiAgcG9wdWxhdGlvbnMgPC0gdW5saXN0KGxhcHBseShyb3duYW1lcyh2Y2Zfb3V0KSwgZnVuY3Rpb24oc2FtcGxlKXsKICBtZXRhJFBvcHVsYXRpb25bbWV0YSRTYW1wbGUgPT0gc2FtcGxlXQogIH0pKQogIHZjZl9vdXQkcG9wdWxhdGlvbiA8LSBwb3B1bGF0aW9ucwogICMgcmVvcmRlcgogIHZjZl9vdXQgPC0gdmNmX291dCAlPiUgZHBseXI6OnNlbGVjdChwb3B1bGF0aW9uLCBldmVyeXRoaW5nKCkpCiAgIyBzcGxpdCBieSBwb3B1bGF0aW9uCiAgdmNmX291dF9zcGxpdCA8LSBzcGxpdCh2Y2Zfb3V0LCBmID0gdmNmX291dCRwb3B1bGF0aW9uKQogICMgcmVtb3ZlIHBvcHVsYXRpb24gY29sdW1ucwogIHZjZl9vdXRfc3BsaXQgPC0gbGFwcGx5KHZjZl9vdXRfc3BsaXQsIGZ1bmN0aW9uKHgpewogICAgeCRwb3B1bGF0aW9uIDwtIE5VTEwKICAgIHJldHVybih4KQogIH0pCiAgIyBnZXQgYWxsZWxlIGNvdW50cwogIGFsbGVsZV9jb3VudHMgPC0gbGFwcGx5KHZjZl9vdXRfc3BsaXQsIGZ1bmN0aW9uKHBvcHVsYXRpb24pewogICAgc3VtbWFyeShwb3B1bGF0aW9uKQogIH0pCiAgcmV0dXJuKGFsbGVsZV9jb3VudHMpCn0pCiMgc2V0IG5hbWVzCm5hbWVzKHZjZl9saXN0KSA8LSBnc3ViKCJzbnBoaXRzX3wudmNmLmd6IiwgIiIsIGxpc3QuZmlsZXMoaGVyZTo6aGVyZSgiZGF0YSIpLCBwYXR0ZXJuID0gZ2xvYjJyeCgic25waGl0c18qLmd6IikpWzE6M10pCgpgYGAKCiMjIFJlYWQgaW4gU05QIGRhdGEKCioqTk9URSoqOgoK4oCiIEluIHRoZSAqTGVlIGV0IGFsLiogKGVkdV95ZWFycykgZGF0YSB0YWJsZXMsIHRoZSBzaGVldCBzYXlzOiAiTm90ZXM6IENsdW1waW5nIG9mIEdXQVMgcmVzdWx0cyB3YXMgcGVyZm9ybWVkIGFzIGRlc2NyaWJlZCBpbiB0aGUgU3VwcGxlbWVudGFyeSBOb3Rlcy4gU05QcyBhcmUgb3JkZXJlZCBieSBQLXZhbHVlLiBDaHJvbW9zb21lIChDSFIpIGFuZCBiYXNlIHBhaXIgKEJQKSBwb3NpdGlvbnMgYXJlIHJlcG9ydGVkIGZvciBodW1hbiBnZW5vbWUgYnVpbGQgMzcgKGhnMTkpLiAqIkFsbGVsZSAxIiBpcyB0aGUgYWxsZWxlIHdob3NlIGVzdGltYXRlZCBlZmZlY3Qgc2l6ZSAoQmV0YSkgYW5kIGFsbGVsZSBmcmVxdWVuY3kgYXJlIHJlcG9ydGVkLiogU3RhbmRhcmQgZXJyb3JzIChTRSkgYW5kIFAtdmFsdWVzIGFyZSBkZXJpdmVkIGZyb20gdGVzdCBzdGF0aXN0aWNzIHRoYXQgaGF2ZSBiZWVuIGFkanVzdGVkIGJ5IGFuIGVzdGltYXRlZCBMRFNDIGludGVyY2VwdCBvZiAxLjExMy4gVGhlIGFuYWx5c2lzIGlzIGJhc2VkIG9uIDEwLDAxNiwyNjUgU05Qcy4gVGhlIGF2ZXJhZ2UgY2hpLXNxdWFyZWQgc3RhdGlzdGljIGlzIDIuNTMwLzIuODE2IChhZGp1c3RlZCBhbmQgdW5hZGp1c3RlZCwgcmVzcGVjdGl2ZWx5KSBhbmQgbGFtYmRhIEdDIGlzIDIuMDM4ICh1bmFkanVzdGVkKS4iCgrigKIgSW4gdGhlICpZZW5nbyBldCBhbC4qIChoZWlnaHQpIGRhdGEgdGFibGVzLCB0aGUgaGVhZGVyIGlzIHNlbGYtZXhwbGFuYXRvcnkuCgrigKIgSW4gdGhlICpIdWFuZyBldCBhbC4qIChpYmQpIGRhdGEgdGFibGVzLCB0aGUgU05QcyBoYXZlIG9kZHMgcmF0aW9zIHJhdGhlciB0aGFuIGJldGFzLiBBbHNvLCB0aGUgbGVnZW5kIHNheXMgdGhhdCAqQTAgaXMgdGhlIHJlZmVyZW5jZSBhbGxlbGUgYW5kIEExIGlzIHRoZSB0ZXN0ZWQgYWxsZWxlKi4gcF9tdWx0aSBpcyB0aGUgLWxvZzEwKFAtdmFsdWUpIGZvciBtdWx0aS12YXJpYXRlIG1vZGVsLgoKYGBge3J9CiMgY3JlYXRlIGVtcHR5IGxpc3QKc25wc19saXN0IDwtIGxpc3QoKQojIGFkZCBlZHVfeWVhcnMgU05QcwpzbnBzX2xpc3RbWyJlZHVfeWVhcnMiXV0gPC0gcmVhZF94bHN4KGhlcmU6OmhlcmUoImRhdGEiLCAiMjAxODA3MjNfTGVlLWV0LWFsX3N1cHAtdGFibGVzLnhsc3giKSwgc2hlZXQgPSAiMi4gRWR1WWVhcnMgTGVhZCBTTlBzIiwgc2tpcCA9IDEsIG5fbWF4ID0gMTI3MSkgJT4lIAogIGRwbHlyOjpzZWxlY3Qoc25wID0gIlNOUCIsIAogICAgICAgICAgICAgICAgdGVzdGVkX2FsbGVsZSA9ICJBbGxlbGUgMSIsIAogICAgICAgICAgICAgICAgb3RoZXJfYWxsZWxlID0gIkFsbGVsZTIiLCAKICAgICAgICAgICAgICAgIGVmZmVjdF9zaXplID0gIkVmZmVjdCBzaXplIiwgCiAgICAgICAgICAgICAgICBwX3ZhbHVlID0gIlAtdmFsdWUiKQojIGFkZCBoZWlnaHQgU05QcwpzbnBzX2xpc3RbWyJoZWlnaHQiXV0gPC0gcmVhZF9kZWxpbShoZXJlOjpoZXJlKCJkYXRhIiwgIjIwMTgxMDE1X1llbmdvLWV0LWFsX3NucHNfaGVpZ2h0LnR4dCIpLCBkZWxpbSA9ICJcdCIpICU+JSAKICBkcGx5cjo6c2VsZWN0KHNucCA9ICJTTlAiLCAKICAgICAgICAgICAgICAgIHRlc3RlZF9hbGxlbGUgPSAiVGVzdGVkX0FsbGVsZSIsIAogICAgICAgICAgICAgICAgb3RoZXJfYWxsZWxlID0gIk90aGVyX0FsbGVsZSIsIAogICAgICAgICAgICAgICAgZWZmZWN0X3NpemUgPSAiQkVUQSIsIAogICAgICAgICAgICAgICAgcF92YWx1ZSA9ICJQIikKIyBhZGQgaWJkIFNOUHMgKGZ1bGwgbGlzdCkKc25wc19saXN0W1siaWJkX2Z1bGwiXV0gPC0gcmVhZF94bHN4KGhlcmU6OmhlcmUoImRhdGEiLCAiMjAxNzA2MjhfSHVhbmctZXQtYWxfc3VwcC10YWJsZS0xLnhsc3giKSwgc2hlZXQgPSAibGlzdCBvZiB2YXJpYW50cyIpICU+JSAKICBkcGx5cjo6bXV0YXRlKG1lYW5fb3IgPSAoT1JfQ0QgKyBPUl9VQykgLyAyKSAlPiUgCiAgZHBseXI6OnNlbGVjdChzbnAgPSAidmFyaWFudCIsIAogICAgICAgICAgICAgICAgdGVzdGVkX2FsbGVsZSA9ICJBMSIsIAogICAgICAgICAgICAgICAgb3RoZXJfYWxsZWxlID0gIkEwIiwgCiAgICAgICAgICAgICAgICBlZmZlY3Rfc2l6ZSA9ICAibWVhbl9vciIsCiAgICAgICAgICAgICAgICBwX3ZhbHVlID0gIlBfbWVhbl85NSIpCiMgYWRkIGliZCBTTlBTIC0gbm90ZSB0aGUgImVmZmVjdCBzaXplIiBoZXJlIGlzIHRoZSBtZWFuIG9kZHMgcmF0aW8gYWNyb3NzIGJvdGggQ0QgYW5kIFVDCnNucHNfbGlzdFtbImliZCJdXSA8LSByZWFkX3hsc3goaGVyZTo6aGVyZSgiZGF0YSIsICIyMDE3MDYyOF9IdWFuZy1ldC1hbF9zdXBwLXRhYmxlLTEueGxzeCIpLCBzaGVldCA9ICJsaXN0IG9mIGNyZWRpYmxlIHNldHMiKSAlPiUgCiAgZHBseXI6Om11dGF0ZShtZWFuX29yID0gKE9SX0NEICsgT1JfVUMpIC8gMikgJT4lIAogIGRwbHlyOjpzZWxlY3Qoc25wID0gInZhcmlhbnQubGVhZCIsIAogICAgICAgICAgICAgICAgdGVzdGVkX2FsbGVsZSA9ICJBMSIsIAogICAgICAgICAgICAgICAgb3RoZXJfYWxsZWxlID0gIkEwIiwgCiAgICAgICAgICAgICAgICBlZmZlY3Rfc2l6ZSA9ICAibWVhbl9vciIsCiAgICAgICAgICAgICAgICBwX3ZhbHVlID0gInBfbXVsdGkiKQoKYGBgCgojIyMgRmlsdGVyIG91dCBpbmRlbHMgZnJvbSBmdWxsIElCRCBsaXN0CgpgYGB7cn0KIyBjcmVhdGUgdmVjdG9yIG9mIGluZGVscwppbmRlbF90ZXN0ZWQgPC0gd2hpY2gobmNoYXIoc25wc19saXN0W1siaWJkX2Z1bGwiXV0kdGVzdGVkX2FsbGVsZSkgPiAxKQppbmRlbF9vdGhlciA8LSB3aGljaChuY2hhcihzbnBzX2xpc3RbWyJpYmRfZnVsbCJdXSRvdGhlcl9hbGxlbGUpID4gMSkKaWJkX2luZGVscyA8LSB1bmlxdWUoc29ydChjKGluZGVsX3Rlc3RlZCwgaW5kZWxfb3RoZXIpKSkKICAgICAgCiMgZmlsdGVyCnNucHNfbGlzdFtbImliZF9mdWxsIl1dIDwtIGRwbHlyOjpzbGljZShzbnBzX2xpc3RbWyJpYmRfZnVsbCJdXSwgKGliZF9pbmRlbHMgKiAtMSkpCmBgYAoKIyMgQ3JlYXRlIGRhdGEgZnJhbWVzIHdpdGggYWxsZWxlIGZyZXF1ZW5jaWVzCgpgYGB7cn0KIyBURVNUCnRlc3RfdmNmX3BvcG4gPC0gdmNmX2xpc3Qkc25waGl0c19lZHV5cnMudmNmLmd6JEFDQiRyczc4MDU2OQp0ZXN0X3NucHMgPC0gc25wc19saXN0JGVkdV95ZWFycyAKCnNucF9pZCA8LSBuYW1lcyh0ZXN0X3ZjZl9wb3BuKVsxXQojIGNyZWF0ZSBmaW5hbCBERgpmaW5hbF9kZiA8LSBkYXRhLmZyYW1lKCJhbGxlbGUiID0gbmFtZXModGVzdF92Y2ZfcG9wbiRhbGxlbGUpLAogICAgICAgICAgICAgICAgICAgICAgICJjb3VudCIgPSB0ZXN0X3ZjZl9wb3BuJGFsbGVsZSwKICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKIyBnZXQgYWxsZWxlIGZyZXF1ZW5jeQpmaW5hbF9kZiRhbF9mcmVxIDwtIGZpbmFsX2RmJGNvdW50IC8gc3VtKGZpbmFsX2RmJGNvdW50KQojIHB1bGwgb3V0IGVmZmVjdCBzaXplcyBhbmQgcC12YWx1ZXMgZnJvbSBzbnBfZGYKZWZmZWN0X3NpemVzIDwtIGRhdGEuZnJhbWUoImFsbGVsZSIgPSBjKHRlc3Rfc25wcyRvdGhlcl9hbGxlbGVbdGVzdF9zbnBzJHNucCA9PSBzbnBfaWRdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGVzdF9zbnBzJHRlc3RlZF9hbGxlbGVbdGVzdF9zbnBzJHNucCA9PSBzbnBfaWRdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImVmZmVjdF9zaXplIiA9IGMoMCwgdGVzdF9zbnBzJGVmZmVjdF9zaXplW3Rlc3Rfc25wcyRzbnAgPT0gc25wX2lkXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJwX3ZhbHVlIiA9IGMoMCwgdGVzdF9zbnBzJHBfdmFsdWVbdGVzdF9zbnBzJHNucCA9PSBzbnBfaWRdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCgojIENyZWF0ZSBmdW5jdGlvbgpnZXRfYWxmcmVxX3RhYmxlIDwtIGZ1bmN0aW9uKHBvcHVsYXRpb24sIHNucF9kZil7CiAgY291bnRlciA8LSAwCiAgcG9wdWxhdGlvbiA8LSBsYXBwbHkocG9wdWxhdGlvbiwgZnVuY3Rpb24oc25wX3N1bW1hcnkpewogICAgIyBzZXQgY291bnRlciBhbmQgZXh0cmFjdCBTTlAgSUQKICAgIGNvdW50ZXIgPDwtIGNvdW50ZXIgKyAxCiAgICBzbnBfaWQgPC0gbmFtZXMocG9wdWxhdGlvbilbY291bnRlcl0KICAgICMgY3JlYXRlIGZpbmFsIERGCiAgICBmaW5hbF9kZiA8LSBkYXRhLmZyYW1lKCJhbGxlbGUiID0gbmFtZXMoc25wX3N1bW1hcnkkYWxsZWxlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgImNvdW50IiA9IHNucF9zdW1tYXJ5JGFsbGVsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCiAgICAjIGdldCBhbGxlbGUgZnJlcXVlbmN5CiAgICBmaW5hbF9kZiRhbF9mcmVxIDwtIGZpbmFsX2RmJGNvdW50IC8gc3VtKGZpbmFsX2RmJGNvdW50KQogICAgIyBwdWxsIG91dCBlZmZlY3Qgc2l6ZXMgYW5kIHAtdmFsdWVzIGZyb20gc25wX2RmCiAgICBlZmZlY3Rfc2l6ZXMgPC0gZGF0YS5mcmFtZSgiYWxsZWxlIiA9IGMoc25wX2RmJG90aGVyX2FsbGVsZVtzbnBfZGYkc25wID09IHNucF9pZF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc25wX2RmJHRlc3RlZF9hbGxlbGVbc25wX2RmJHNucCA9PSBzbnBfaWRdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJlZmZlY3Rfc2l6ZSIgPSBjKDAsIHNucF9kZiRlZmZlY3Rfc2l6ZVtzbnBfZGYkc25wID09IHNucF9pZF0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBfdmFsdWUiID0gYygwLCBzbnBfZGYkcF92YWx1ZVtzbnBfZGYkc25wID09IHNucF9pZF0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCiAgICAjIGpvaW4gREZzLiBOb3RlIHRoYXQgaXQgb25seSBqb2lucyB0aGUgdHdvIGFsbGVsZXMgaW4gdGhlIHNucF9kZgogICAgZmluYWxfZGYgPC0gZHBseXI6OmxlZnRfam9pbihmaW5hbF9kZiwgZWZmZWN0X3NpemVzLCBieSA9ICJhbGxlbGUiKQogICAgcmV0dXJuKGZpbmFsX2RmKQogIH0pCiAgcmV0dXJuKHBvcHVsYXRpb24pCn0KCnRlc3QgPC0gZ2V0X2FsZnJlcV90YWJsZSh2Y2ZfbGlzdCRzbnBoaXRzX2VkdXlycy52Y2YuZ3okQUNCLCBzbnBfZGYgPSBzbnBzX2xpc3QkZWR1X3llYXJzKQojIHdvcmtzCnRlc3QgPC0gbGFwcGx5KHZjZl9saXN0JHNucGhpdHNfZWR1eXJzLnZjZi5neiwgZnVuY3Rpb24ocG9wdWxhdGlvbil7CiAgZ2V0X2FsZnJlcV90YWJsZShwb3B1bGF0aW9uLCBzbnBzX2xpc3QkZWR1X3llYXJzKQp9KQojIHdvcmtzCnRlc3RfaGVpZ2h0IDwtIGxhcHBseSh2Y2ZfbGlzdCRzbnBoaXRzX2hlaWdodC52Y2YuZ3osIGZ1bmN0aW9uKHBvcHVsYXRpb24pewogIGdldF9hbGZyZXFfdGFibGUocG9wdWxhdGlvbiwgc25wc19saXN0JGhlaWdodCkKfSkKIyBFcnJvciBpbiBkYXRhLmZyYW1lKGFsbGVsZSA9IGMoc25wX2RmJG90aGVyX2FsbGVsZVtzbnBfZGYkc25wID09IHNucF9pZF0sICA6IGFyZ3VtZW50cyBpbXBseSBkaWZmZXJpbmcgbnVtYmVyIG9mIHJvd3M6IDAsIDEKIyBGaW5kIG91dCBpZiBhbGwgU05QcyBhcmUgaW4gYm90aCBERnMKd2hpY2gobmFtZXModmNmX2xpc3Qkc25waGl0c19oZWlnaHQudmNmLmd6JEFDQikgJWluJSBzbnBzX2xpc3QkaGVpZ2h0JHNucCA9PSBGKQojIFsxXSAgNDY2IDIwMDIKIyBhZGp1c3QgZnVuY3Rpb24gc28gdGhhdCBpdCBvbmx5IHRha2VzIFNOUHMgdGhhdCBhcmUgaW4gYm90aCBERnMKZ2V0X2FsZnJlcV90YWJsZSA8LSBmdW5jdGlvbihwb3B1bGF0aW9uLCBzbnBfZGYpewogICMgdGFrZSBvbmx5IHRoZSBTTlBzIHRoYXQgYXJlIGFsc28gaW4gdGhlIHNucF9kZgogIGluZGV4ZXNfdG9fa2VlcCA8LSB3aGljaChuYW1lcyhwb3B1bGF0aW9uKSAlaW4lIHNucF9kZiRzbnAgPT0gVCkKICBwb3B1bGF0aW9uIDwtIHBvcHVsYXRpb25baW5kZXhlc190b19rZWVwXQogICMgc3RhcnQgbG9vcAogIGNvdW50ZXIgPC0gMAogIHBvcHVsYXRpb24gPC0gbGFwcGx5KHBvcHVsYXRpb24sIGZ1bmN0aW9uKHNucF9zdW1tYXJ5KXsKICAgICMgc2V0IGNvdW50ZXIgYW5kIGV4dHJhY3QgU05QIElECiAgICBjb3VudGVyIDw8LSBjb3VudGVyICsgMQogICAgc25wX2lkIDwtIG5hbWVzKHBvcHVsYXRpb24pW2NvdW50ZXJdCiAgICAjIGNyZWF0ZSBmaW5hbCBERgogICAgZmluYWxfZGYgPC0gZGF0YS5mcmFtZSgiYWxsZWxlIiA9IG5hbWVzKHNucF9zdW1tYXJ5JGFsbGVsZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICJjb3VudCIgPSBzbnBfc3VtbWFyeSRhbGxlbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQogICAgIyBnZXQgYWxsZWxlIGZyZXF1ZW5jeQogICAgZmluYWxfZGYkYWxfZnJlcSA8LSBmaW5hbF9kZiRjb3VudCAvIHN1bShmaW5hbF9kZiRjb3VudCkKICAgICMgcHVsbCBvdXQgZWZmZWN0IHNpemVzIGFuZCBwLXZhbHVlcyBmcm9tIHNucF9kZgogICAgZWZmZWN0X3NpemVzIDwtIGRhdGEuZnJhbWUoImFsbGVsZSIgPSBjKHNucF9kZiRvdGhlcl9hbGxlbGVbc25wX2RmJHNucCA9PSBzbnBfaWRdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNucF9kZiR0ZXN0ZWRfYWxsZWxlW3NucF9kZiRzbnAgPT0gc25wX2lkXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiZWZmZWN0X3NpemUiID0gYygwLCBzbnBfZGYkZWZmZWN0X3NpemVbc25wX2RmJHNucCA9PSBzbnBfaWRdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwX3ZhbHVlIiA9IGMoMCwgc25wX2RmJHBfdmFsdWVbc25wX2RmJHNucCA9PSBzbnBfaWRdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQogICAgIyBqb2luIERGcy4gTm90ZSB0aGF0IGl0IG9ubHkgam9pbnMgdGhlIHR3byBhbGxlbGVzIGluIHRoZSBzbnBfZGYKICAgIGZpbmFsX2RmIDwtIGRwbHlyOjpsZWZ0X2pvaW4oZmluYWxfZGYsIGVmZmVjdF9zaXplcywgYnkgPSAiYWxsZWxlIikKICAgIHJldHVybihmaW5hbF9kZikKICB9KQogIHJldHVybihwb3B1bGF0aW9uKQp9CgojIyBOZWVkIHRvIGFkanVzdCBlZmZlY3Rfc2l6ZSBkaXJlY3Qgd2l0aGluIHRoaXMgZnVuY3Rpb24KdGVzdDIgPC0gbGFwcGx5KHRlc3RfYWxjbnRzLCBmdW5jdGlvbih4KXsKICBpZihhbnkoeCRlZmZlY3Rfc2l6ZSA8IDApKXsKICAgIHgkZWZmZWN0X3NpemVbeCRlZmZlY3Rfc2l6ZSA9PSAwXSA8LSB4JGVmZmVjdF9zaXplW3gkZWZmZWN0X3NpemUgPCAwXSAqICgtMSkKICAgIHgkZWZmZWN0X3NpemVbeCRlZmZlY3Rfc2l6ZSA8IDBdIDwtIDAKICAgIHJldHVybih4KQogIH0KICBlbHNlIHsKICAgIHJldHVybih4KQogIH0KfSkKIyBXT1JLUwp0ZXN0IDwtIHRlc3RfYWxjbnRzW1sxXV0KaWYoYW55KHRlc3QkZWZmZWN0X3NpemUgPCAwKSl7CiAgdGVzdCRlZmZlY3Rfc2l6ZVt0ZXN0JGVmZmVjdF9zaXplID09IDBdIDwtIHRlc3QkZWZmZWN0X3NpemVbdGVzdCRlZmZlY3Rfc2l6ZSA8IDBdICogKC0xKQogIHRlc3QkZWZmZWN0X3NpemVbdGVzdCRlZmZlY3Rfc2l6ZSA8IDBdIDwtIDAKfQojIEluY29ycG9yYXRlIGludG8gZnVuY3Rpb246CmdldF9hbGZyZXFfdGFibGUgPC0gZnVuY3Rpb24ocG9wdWxhdGlvbiwgc25wX2RmKXsKICAjIHRha2Ugb25seSB0aGUgU05QcyB0aGF0IGFyZSBhbHNvIGluIHRoZSBzbnBfZGYKICBpbmRleGVzX3RvX2tlZXAgPC0gd2hpY2gobmFtZXMocG9wdWxhdGlvbikgJWluJSBzbnBfZGYkc25wID09IFQpCiAgcG9wdWxhdGlvbiA8LSBwb3B1bGF0aW9uW2luZGV4ZXNfdG9fa2VlcF0KICAjIHN0YXJ0IGxvb3AKICBjb3VudGVyIDwtIDAKICBwb3B1bGF0aW9uIDwtIGxhcHBseShwb3B1bGF0aW9uLCBmdW5jdGlvbihzbnBfc3VtbWFyeSl7CiAgICAjIHNldCBjb3VudGVyIGFuZCBleHRyYWN0IFNOUCBJRAogICAgY291bnRlciA8PC0gY291bnRlciArIDEKICAgIHNucF9pZCA8LSBuYW1lcyhwb3B1bGF0aW9uKVtjb3VudGVyXQogICAgIyBjcmVhdGUgZmluYWwgREYKICAgIGZpbmFsX2RmIDwtIGRhdGEuZnJhbWUoImFsbGVsZSIgPSBuYW1lcyhzbnBfc3VtbWFyeSRhbGxlbGUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAiY291bnQiID0gc25wX3N1bW1hcnkkYWxsZWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKICAgICMgZ2V0IGFsbGVsZSBmcmVxdWVuY3kKICAgIGZpbmFsX2RmJGFsX2ZyZXEgPC0gZmluYWxfZGYkY291bnQgLyBzdW0oZmluYWxfZGYkY291bnQpCiAgICAjIHB1bGwgb3V0IGVmZmVjdCBzaXplcyBhbmQgcC12YWx1ZXMgZnJvbSBzbnBfZGYKICAgIGVmZmVjdF9zaXplcyA8LSBkYXRhLmZyYW1lKCJhbGxlbGUiID0gYyhzbnBfZGYkb3RoZXJfYWxsZWxlW3NucF9kZiRzbnAgPT0gc25wX2lkXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzbnBfZGYkdGVzdGVkX2FsbGVsZVtzbnBfZGYkc25wID09IHNucF9pZF0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImVmZmVjdF9zaXplIiA9IGMoMCwgc25wX2RmJGVmZmVjdF9zaXplW3NucF9kZiRzbnAgPT0gc25wX2lkXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicF92YWx1ZSIgPSBjKDAsIHNucF9kZiRwX3ZhbHVlW3NucF9kZiRzbnAgPT0gc25wX2lkXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRikKICAgICMgam9pbiBERnMuIE5vdGUgdGhhdCBpdCBvbmx5IGpvaW5zIHRoZSB0d28gYWxsZWxlcyBpbiB0aGUgc25wX2RmCiAgICBmaW5hbF9kZiA8LSBkcGx5cjo6bGVmdF9qb2luKGVmZmVjdF9zaXplcywgZmluYWxfZGYsIGJ5ID0gImFsbGVsZSIpCiAgICAjIHJlbW92ZSBhbnkgcm93cyB3aXRoIE5BIChjYXVzZWQgYnkgaGF2aW5nIGEgdGhpcmQgYWxsZWxlKQogICAgZmluYWxfZGYgPC0gZmluYWxfZGZbY29tcGxldGUuY2FzZXMoZmluYWxfZGYpLCBdCiAgICAjIGNvbnZlcnQgbmVnYXRpdmUgZWZmZWN0cyBzaXplcyBpbnRvIHBvc2l0aXZlIG9uZXMgYnkgZmxpcHBpbmcgdGhlIGFsbGVsZQogICAgaWYoYW55KGZpbmFsX2RmJGVmZmVjdF9zaXplIDwgMCkpewogICAgICBmaW5hbF9kZiRlZmZlY3Rfc2l6ZVtmaW5hbF9kZiRlZmZlY3Rfc2l6ZSA9PSAwXSA8LSBmaW5hbF9kZiRlZmZlY3Rfc2l6ZVtmaW5hbF9kZiRlZmZlY3Rfc2l6ZSA8IDBdICogKC0xKQogICAgICBmaW5hbF9kZiRlZmZlY3Rfc2l6ZVtmaW5hbF9kZiRlZmZlY3Rfc2l6ZSA8IDBdIDwtIDAKICAgIH0KICAgIHJldHVybihmaW5hbF9kZikKICB9KQogIHJldHVybihwb3B1bGF0aW9uKQp9CgojIFJ1biBvdmVyIGxpc3QKY291bnRlcl9uZXcgPC0gMAphbGNudF9kZl9sc3QgPC0gbGFwcGx5KHZjZl9saXN0LCBmdW5jdGlvbihwaGVubyl7CiAgY291bnRlcl9uZXcgPDwtIGNvdW50ZXJfbmV3ICsgMQogIGxhcHBseShwaGVubywgZnVuY3Rpb24oeCl7CiAgICBvdXQgPC0gZ2V0X2FsZnJlcV90YWJsZShwb3B1bGF0aW9uID0geCwgc25wX2RmID0gc25wc19saXN0W1tjb3VudGVyX25ld11dKQogICAgZmluYWwgPC0gZHBseXI6OmJpbmRfcm93cyhvdXQsIC5pZCA9ICJzbnAiKQogICAgcmV0dXJuKGZpbmFsKQogIH0pCn0pCgojIyMgQ3JlYXRpbmcgc29tZSBlcnJvcnMgd2l0aCBmdWxsIElCRCBsaXN0LiBUcm91Ymxlc2hvb3Q6CmFsY250X2RmX2xzdCA8LSBsYXBwbHkodmNmX2xpc3RbWzNdXSwgZnVuY3Rpb24ocGhlbm8pewogIGxhcHBseShwaGVubywgZnVuY3Rpb24oeCl7CiAgICB0cnkoZ2V0X2FsZnJlcV90YWJsZShwb3B1bGF0aW9uID0geCwgc25wX2RmID0gc25wc19saXN0W1szXV0pKQogICAgI2ZpbmFsIDwtIGRwbHlyOjpiaW5kX3Jvd3Mob3V0LCAuaWQgPSAic25wIikKICAgICNyZXR1cm4oZmluYWwpCiAgfSkKfSkKCiMgY3JlYXRlIGZpbmFsIERGCmFsY250X2RmIDwtIGxhcHBseShhbGNudF9kZl9sc3QsIGZ1bmN0aW9uKHBoZW5vKXsKICBkcGx5cjo6YmluZF9yb3dzKHBoZW5vLCAuaWQgPSAicG9wdWxhdGlvbiIpCn0pCgojIGZpbHRlciBvbmx5IGZvciBhbGxlbGVzIHdpdGggYW4gZWZmZWN0IGFuZCBjb3JyZWN0IHJvd25hbWVzCmFsY250X2RmX2ZpbHQgPC0gbGFwcGx5KGFsY250X2RmLCBmdW5jdGlvbihwaGVubyl7CiAgcGhlbm8gPC0gcGhlbm9bcGhlbm8kZWZmZWN0X3NpemUgIT0gMCwgXQogIHJvd25hbWVzKHBoZW5vKSA8LSBzZXEoMTpucm93KHBoZW5vKSkKICByZXR1cm4ocGhlbm8pCn0pICAKYGBgCgojIyBDcmVhdGUgY29sdW1ucyBmb3IgZWFjaCBwb3B1bGF0aW9uCgpgYGB7cn0KcGxvdF9kZl9sc3QgPC0gbGFwcGx5KGFsY250X2RmX2ZpbHQsIGZ1bmN0aW9uKHBoZW5vKXsKICBwaGVubyA8LSBwaGVubyAlPiUgZHBseXI6OnNlbGVjdCgtY291bnQpCiAgcGhlbm8gPC0gdGlkeXI6OnBpdm90X3dpZGVyKGRhdGEgPSBwaGVubywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZXNfZnJvbSA9IHBvcHVsYXRpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWVzX3ByZWZpeCA9ICJhbF9mcmVxXyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlc19mcm9tID0gYWxfZnJlcSkKICByZXR1cm4ocGhlbm8pCn0pCgpgYGAKCiMjIFBsb3QKCiMjIyBTZXQgdXAgdmVjdG9ycyBmb3IgcGhlbm90eXBlLXNwZWNpZmljIHBhcmFtZXRlcnMKCmBgYHtyfQojIENvbG91ciBwYWxldHRlcwpjb2xvdXJfcGFscyA8LSBjKCJ2aXJpZGlzIiwgImluZmVybm8iLCAicGxhc21hIikKIyBMaW5lIGNvbG91cnMKbGluZV9jb2xzIDwtIGMoInJlZCIsICJyZWQiLCAiYmx1ZSIpCiMgVGl0bGVzCnRpdGxlcyA8LSBjKCJFZHVjYXRpb25hbCBBdHRhaW5tZW50IiwgIkhlaWdodCIsICJJbmZsYW1tYXRvcnkgQm93ZWwgRGlzZWFzZSIpCiMgTGVnZW5kIHRpdGxlIGZvciBlZmZlY3Qgc2l6ZQpsZWdlbmRfdGl0bGUgPC0gYygiRWZmZWN0IHNpemUgKEJldGEpIiwgIkVmZmVjdCBzaXplIChCZXRhKSIsICJNZWFuIG9kZHMgcmF0aW9cbihDRCBhbmQgVUMpIikKYGBgCgojIyMgWVJJIHYgQ0VVCgpgYGB7cn0KY291bnRlciA8LSAwCmxhcHBseShwbG90X2RmX2xzdCwgZnVuY3Rpb24ocGhlbm8pewogIGNvdW50ZXIgPDwtIGNvdW50ZXIgKyAxCiAgZ2dwbG90KHBoZW5vLCBhZXMoYWxfZnJlcV9ZUkksIGFsX2ZyZXFfQ0VVLCBjb2xvdXIgPSBlZmZlY3Rfc2l6ZSkpICsKICAgIGdlb21fcG9pbnQoKSArCiAgICBzY2FsZV9jb2xvcl92aXJpZGlzX2Mob3B0aW9uID0gY29sb3VyX3BhbHNbY291bnRlcl0pICsKICAgIGNvb3JkX2ZpeGVkKCkgKwogICAgZ2VvbV9zbW9vdGgoc2UgPSBGLCBjb2xvdXIgPSBsaW5lX2NvbHNbY291bnRlcl0pICsKICAgIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDAsIHNsb3BlID0gMSwgY29sb3VyID0gImJsdWUiKSArCiAgICB4bGFiKCJBbGxlbGUgZnJlcXVlbmN5IGluIFlSSSIpICsKICAgIHlsYWIoIkFsbGVsZSBmcmVxdWVuY3kgaW4gQ0VVIikgKwogICAgbGFicyh0aXRsZSA9IHRpdGxlc1tjb3VudGVyXSwKICAgICAgICAgY29sb3VyID0gbGVnZW5kX3RpdGxlW2NvdW50ZXJdKQp9KQpgYGAKCiMjIyBZUkkgdiBDSFMKCmBgYHtyfQpjb3VudGVyIDwtIDAKbGFwcGx5KHBsb3RfZGZfbHN0LCBmdW5jdGlvbihwaGVubyl7CiAgY291bnRlciA8PC0gY291bnRlciArIDEKICBnZ3Bsb3QocGhlbm8sIGFlcyhhbF9mcmVxX1lSSSwgYWxfZnJlcV9DSFMsIGNvbG91ciA9IGVmZmVjdF9zaXplKSkgKwogICAgZ2VvbV9wb2ludCgpICsKICAgIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYyhvcHRpb24gPSBjb2xvdXJfcGFsc1tjb3VudGVyXSkgKwogICAgY29vcmRfZml4ZWQoKSArCiAgICBnZW9tX3Ntb290aChzZSA9IEYsIGNvbG91ciA9IGxpbmVfY29sc1tjb3VudGVyXSkgKwogICAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxLCBjb2xvdXIgPSAiYmx1ZSIpICsKICAgIHhsYWIoIkFsbGVsZSBmcmVxdWVuY3kgaW4gWVJJIikgKwogICAgeWxhYigiQWxsZWxlIGZyZXF1ZW5jeSBpbiBDSFMiKSArCiAgICBsYWJzKHRpdGxlID0gdGl0bGVzW2NvdW50ZXJdLAogICAgICAgICBjb2xvdXIgPSBsZWdlbmRfdGl0bGVbY291bnRlcl0pCn0pCmBgYAoKIyBGc3QKCiMjIENyZWF0ZSBkYXRhIGZyYW1lcwoKYGBge3J9CiMgQ3JlYXRlIGxpc3Qgb2YgVkNGcyBidXQgd2l0aG91dCBhbGxlbGUgY291bnRzCnZjZl9saXN0X3JhdyA8LSBsYXBwbHkodGFyZ2V0X3ZjZnMsIGZ1bmN0aW9uKHZjZl9maWxlKXsKICAjIHJlYWQgaW4gVkNGcwogIHZjZl9vdXQgPC0gcGVnYXM6OnJlYWQudmNmKHZjZl9maWxlKQogICMgY3JlYXRlIHBvcHVsYXRpb24gY29sdW1uCiAgcG9wdWxhdGlvbnMgPC0gdW5saXN0KGxhcHBseShyb3duYW1lcyh2Y2Zfb3V0KSwgZnVuY3Rpb24oc2FtcGxlKXsKICBtZXRhJFBvcHVsYXRpb25bbWV0YSRTYW1wbGUgPT0gc2FtcGxlXQogIH0pKQogIHZjZl9vdXQkcG9wdWxhdGlvbiA8LSBwb3B1bGF0aW9ucwogICMgcmVvcmRlcgogIHZjZl9vdXQgPC0gdmNmX291dCAlPiUgZHBseXI6OnNlbGVjdChwb3B1bGF0aW9uLCBldmVyeXRoaW5nKCkpCn0pCm5hbWVzKHZjZl9saXN0X3JhdykgPC0gdGl0bGVzCgojIENyZWF0ZSBsaXN0IG9mIFZDRnMgd2l0aCBzdW1tYXJpZXMKdmNmX2xpc3Rfc3VtbWFyeSA8LSBsYXBwbHkodGFyZ2V0X3ZjZnMsIGZ1bmN0aW9uKHZjZl9maWxlKXsKICAjIHJlYWQgaW4gVkNGcwogIHZjZl9vdXQgPC0gcGVnYXM6OnJlYWQudmNmKHZjZl9maWxlKQogICMgZ2V0IHN1bW1hcnkKICBzdW1tYXJ5KHZjZl9vdXQpCn0pCgojIEZpbmQgb3V0IHdoaWNoIG9uZXMgYXJlbid0IGJpYWxsZWxpYyB0byByZW1vdmUKYmlhbGxlbGljcyA8LSBsYXBwbHkodmNmX2xpc3Rfc3VtbWFyeSwgZnVuY3Rpb24ocGhlbm8pewogIG91dCA8LSB3aGljaChsYXBwbHkocGhlbm8sIGZ1bmN0aW9uKHNucCl7CiAgICBsZW5ndGgoc25wJGFsbGVsZSkgPT0gMgogIH0pID09IFQpCiAgIyBwbHVzIG9uZSB0byBhY2NvdW50IGZvciB0aGUgcG9wdWxhdGlvbiBjb2x1bW4gaW4gdmNmX2xpc3RfcmF3CiAgb3V0ICsgMQp9KQoKIyBSZW1vdmUKY291bnRlciA8LSAwCnZjZl9saXN0X3JhdyA8LSBsYXBwbHkodmNmX2xpc3RfcmF3LCBmdW5jdGlvbihwaGVubyl7CiAgY291bnRlciA8PC0gY291bnRlciArIDEKICBwaGVubyA8LSBwaGVub1tjKDEsIGJpYWxsZWxpY3NbW2NvdW50ZXJdXSldICMgYWRkICIxIiB0byBzdGFydCBvZiB2ZWN0b3IgdG8ga2VlcCBwb3B1bGF0aW9uIGNvbHVtbgp9KQoKIyBUcnkgd2l0aG91dCBwb3B1bGF0aW9uIGNvbHVtbgp2Y2ZfbGlzdF9yYXcgPC0gbGFwcGx5KHRhcmdldF92Y2ZzLCBmdW5jdGlvbih2Y2ZfZmlsZSl7CiAgdmNmX291dCA8LSBwZWdhczo6cmVhZC52Y2YodmNmX2ZpbGUpCn0pCiMgQ3JlYXRlIHZlY3RvciBvZiBwb3B1bGF0aW9ucwpwb3B1bGF0aW9ucyA8LSB1bmxpc3QobGFwcGx5KHJvd25hbWVzKHZjZl9saXN0X3Jhd1tbMV1dKSwgZnVuY3Rpb24oc2FtcGxlKXsKICBtZXRhJFBvcHVsYXRpb25bbWV0YSRTYW1wbGUgPT0gc2FtcGxlXQp9KSkKCiMgR2VuZXJhdGUgRnN0IHN0YXRzCmZzdF9vdXRfbHN0IDwtIGxhcHBseSh2Y2ZfbGlzdF9yYXcsIGZ1bmN0aW9uKHBoZW5vKXsKICBhcy5kYXRhLmZyYW1lKHBlZ2FzOjpGc3QocGhlbm8sIHBvcCA9IHBvcHVsYXRpb25zKSkKfSkKCiMgbWFrZSByb3duYW1lcyBpbnRvIHNlcGFyYXRlIGNvbHVtbgpmc3Rfb3V0X2xzdCA8LSBsYXBwbHkoZnN0X291dF9sc3QsIGZ1bmN0aW9uKHBoZW5vKXsKICBwaGVubyRzbnAgPC0gcm93bmFtZXMocGhlbm8pCiAgcmV0dXJuKHBoZW5vKQp9KQpuYW1lcyhmc3Rfb3V0X2xzdCkgPC0gdGl0bGVzCiMgCmZzdF9vdXRfZGYgPC0gZHBseXI6OmJpbmRfcm93cyhmc3Rfb3V0X2xzdCwgLmlkID0gInBoZW5vdHlwZSIpCmBgYAoKIyMgUGxvdAoKYGBge3J9CmdncGxvdChmc3Rfb3V0X2RmLCBhZXMoRnN0LCBmaWxsID0gcGhlbm90eXBlKSkgKwogIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuNykgKwogIGxhYnMoZmlsbCA9ICJQaGVub3R5cGUiKSArCiAgeWxhYigiRGVuc2l0eSIpICsKICB0aGVtZV9idygpCmBgYAoKKjIwMjAwMzMwKgoKRnJvbSBFQjogIldlIG5lZWQgYSBzdHJvbmdlciBwb3NpdGl2ZSBjb250cm9sIC0gcGVyaGFwcyBjYW4geW91IGdldCBza2luIHBpZ21lbnRhdGlvbiBsb2NpPyIKCiMgUmUtZG8gd2l0aCBza2luIHBpZ21lbnRhdGlvbiBwb3NpdGl2ZSBjb250cm9sCgojIyBGaW5kIHBhcGVycyB3aXRoIGFzc29jaWF0ZWQgU05QcwoK4oCiICoqQ3Jhd2ZvcmQgZXQgYWwuICgyMDE3KSoqIExvY2kgYXNzb2NpYXRlZCB3aXRoIHNraW4gcGlnbWVudGF0aW9uIGlkZW50aWZpZWQgaW4gQWZyaWNhbiBwb3B1bGF0aW9ucywgKlNjaWVuY2UqOiA8aHR0cHM6Ly9zY2llbmNlLnNjaWVuY2VtYWcub3JnL2NvbnRlbnQvMzU4LzYzNjUvZWFhbjg0MzMuYWJzdHJhY3Q/Y2FzYV90b2tlbj02dEdFamN0MW5VUUFBQUFBOklMN0xDei14UTlsNnJMaHhCazVWY0dCandUckVhNVVwQWxDLW5DbDJtdmNBU3U0aVpiV01pdV9VajhZVUhJSVNnQ2liT2QxeWEyNXNPUT4uIFRha2UgdGFibGUgMSwgYW5kIG1hbnVhbGx5IHRyYW5zZm9ybSAnQW5jZXN0cmFsPkRpdmlkZWQnIGNvbHVtbiBpbnRvIG5ldyBjb2x1bW5zIHRpdGxlZCAndGVzdGVkX2FsbGVsZScgKHRoZSBhbGxlbGUgaW4gYm9sZCkgYW5kICdvdGhlcl9hbGxlbGUnLiBTYXZlIGhlcmU6IGBkYXRhLzIwMTcxMTE3X0NyYXdmb3JkLWV0LWFsX1RhYmxlLTEueGxzeGAK4oCiICoqQWRoaWthcmkgZXQgYWwuICgyMDE5KSoqIEEgR1dBUyBpbiBMYXRpbiBBbWVyaWNhbnMgaGlnaGxpZ2h0cyB0aGUgY29udmVyZ2VudCBldm9sdXRpb24gb2YgbGlnaHRlciBza2luIHBpZ21lbnRhdGlvbiBpbiBFdXJhc2lhLCAqTmF0dXJlKjogPGh0dHBzOi8vd3d3Lm5hdHVyZS5jb20vYXJ0aWNsZXMvczQxNDY3LTAxOC0wODE0Ny0wPi4gIlN1bW1hcnkgc3RhdGlzdGljcyBmcm9tIHRoZSBHV0FTIGFuYWx5c2VzIGlzIGRlcG9zaXRlZCBhdCBHV0FTIGNlbnRyYWwgd2l0aCB0aGUgbGluayA8aHR0cDovL3d3dy5nd2FzY2VudHJhbC5vcmcvc3R1ZHkvSEdWU1QzMzA4PiIuIFVuZGVyIHRoZSAnQXNzb2NpYXRpb24gcmVzdWx0cycgdGFiLCB0aGVyZSBpcyBvbmUgZGF0YXNldCBmb3IgZWFjaCBvZiB0aGUgNiBwaGVub3R5cGVzIHRlc3RlZDogIAogIC0gTWVsYW5pbiBpbmRleAogIC0gSGFpciBjb2xvcgogIC0gRXllIGNvbG9yCiAgLSBEaWdpdGFsIGV5ZSBjb2xvcjogTCAobGlnaHRuZXNzKQogIC0gRGlnaXRhbCBleWUgY29sb3I6IEMgKGNocm9tYSkKICAtIERpZ2l0YWwgZXllIGNvbG9yOiBjb3NIIChjb3NpbmUgb2YgaHVlKQpEaWZmaWN1bHQgdG8gYXNjZXJ0YWluIHdoaWNoIG9uZXMgaGFkIGdlbm9tZS13aWRlIHNpZ25pZmljYW5jZS4KSW5zdGVhZCwgcHVsbCB0YWJsZXMgZGlyZWN0bHkgZnJvbSBwYXBlciBhbmQgc3VwcGxlbWVudGFyeSBtYXRlcmlhbHMsIGFuZCBwdXQgaGVyZSBpbiBkaWZmZXJlbnQgc2hlZXRzOiBgZGF0YS8yMDE5MDEyMV9BZGhpa2FyaS1ldC1hbF9zbnBzLnhsc3hgCiAgLSAqVGFibGUgMSo6IDE4IGxlYWQgU05QcyBmcm9tIHBhcGVyLCBlYWNoIHdpdGggYSBkaWZmZXJlbnQgcC12YWx1ZSBmb3Igb25lIG9mIHRoZSA2IHBoZW5vdHlwZXMuCiAgLSAqc3VwcF90YWJsZV82KjogMTEgU05QcyBhc3NvY2lhdGVkIHdpdGggY29tYmluZWQgdHJhaXRzLgogIC0gKnN1cHBfdGFibGVfMTIqOiAxNjEgU05QcyBjb2xsYWd0ZWQgZnJvbSBwdWJsaXNoZWQgYXNzb2NpYXRpb24gc3R1ZGllcyBvbiBwaWdtZW50YXRpb24uIFNlZSB0YWJsZSBmb3IgcmVmZXJlbmNlcywgd2hpY2ggd2VyZSB1c2VkIHRvIGlkZW50aWZ5IG90aGVyIHBpZ21lbnRhdGlvbiBHV0FTIHN0dWRpZXMuCuKAoiAqKkhlcm5hbmRlei1QYWNoZWNvIGV0IGFsLiAoMjAxNykqKiBJZGVudGlmaWNhdGlvbiBvZiBhIG5vdmVsIGxvY3VzIGFzc29jaWF0ZWQgd2l0aCBza2luIGNvbG91ciBpbiBBZnJpY2FuLWFkbWl4ZWQgcG9wdWxhdGlvbnMsICpTY2llbnRpZmljIFJlcG9ydHMqOiA8aHR0cHM6Ly93d3cubmF0dXJlLmNvbS9hcnRpY2xlcy9zcmVwNDQ1NDg+LiA5IGhpdHMgd2l0aCBnZW5vbWUtd2lkZSBzaWduaWZpY2FuY2UgaGVyZTogYGRhdGEvMjAxNzAzMTZfSGVybmFuZGV6LVBhY2hlY28tZXQtYWwueGxzeGAuCgpPdGhlcnMgY29tcGlsZWQgaW50byB0aGUgc2luZ2xlIFhMU1ggZG9jIGBkYXRhLzIwMjAwNjIyX3BpZ21lbnRhdGlvbl9zbnBzLnhsc3hgOgrigKIgJzIwMTkwMzIxX0pvbm5hbGFnYWRkYS1ldC1hbCc6ICoqSm9ubmFsYWdhZGRhIGV0IGFsLiAoMjAxOSkqKiBBIEdlbm9tZS1XaWRlIEFzc29jaWF0aW9uIFN0dWR5IG9mIFNraW4gYW5kIElyaXMgUGlnbWVudGF0aW9uIGFtb25nIEluZGl2aWR1YWxzIG9mIFNvdXRoIEFzaWFuIEFuY2VzdHJ5LCAqR2Vub21lIEJpb2xvZ3kgYW5kIEV2b2x1dGlvbio6IDxodHRwczovL2FjYWRlbWljLm91cC5jb20vZ2JlL2FydGljbGUvMTEvNC8xMDY2LzU0MTYxNDc+LiBUb29rIDkgU05QcyBhc3NvY2lhdGVkIHdpdGggaXJpcyBjb2xvdXIgZnJvbSBUYWJsZSAxLCBhbmQgMTQgU05QcyBkZXNjcmliZWQgaW4gcHJldmlvdXMgc3R1ZGllcyBmcm9tIFRhYmxlIDIuCuKAoiAnMjAxNzExMzBfTWFydGluLWV0LWFsJzogKipNYXJ0aW4gZXQgYWwuICgyMDE3KSoqIEFuIFVuZXhwZWN0ZWRseSBDb21wbGV4IEFyY2hpdGVjdHVyZSBmb3IgU2tpbiBQaWdtZW50YXRpb24gaW4gQWZyaWNhbnMsICpDZWxsKjogPGh0dHBzOi8vd3d3LmNlbGwuY29tL2NlbGwvZnVsbHRleHQvUzAwOTItODY3NCUyODE3JTI5MzEzMjQtNz4uIFRvb2sgYWxsIDQyIFNOUHMgZnJvbSBUYWJsZSBTNkEgaW4gU3VwcGxlbWVudGFsIEluZm9ybWF0aW9uIGhlcmU6IDxodHRwczovL3d3dy5jZWxsLmNvbS9jbXMvMTAuMTAxNi9qLmNlbGwuMjAxNy4xMS4wMTUvYXR0YWNobWVudC9lY2NlYWQ4My0xYTk2LTQ0NDQtOTAzMi1mOTY4ZWU0ODFkMTUvbW1jMi54bHN4Pi4K4oCiICcyMDE1MDUxMl9MaXUtZXQtYWwnOiAqKkxpdSBldCBhbC4gKDIwMTUpKiogR2VuZXRpY3Mgb2Ygc2tpbiBjb2xvciB2YXJpYXRpb24gaW4gRXVyb3BlYW5zOiBnZW5vbWUtd2lkZSBhc3NvY2lhdGlvbiBzdHVkaWVzIHdpdGggZnVuY3Rpb25hbCBmb2xsb3ctdXAsICpIdW1hbiBHZW5ldGljcyo6IDxodHRwczovL2xpbmsuc3ByaW5nZXIuY29tL2FydGljbGUvMTAuMTAwNyUyRnMwMDQzOS0wMTUtMTU1OS0wPi4gVG9vayBhbGwgOSBTTlBzIGZyb20gVGFibGUgMS4K4oCiICcyMDEyMTAzMV9DYW5kaWxsZS1ldC1hbCc6ICoqQ2FuZGlsbGUgZXQgYWwuICgyMDEyKSoqIEdlbm9tZS13aWRlIGFzc29jaWF0aW9uIHN0dWRpZXMgb2YgcXVhbnRpdGF0aXZlbHkgbWVhc3VyZWQgc2tpbiwgaGFpciwgYW5kIGV5ZSBwaWdtZW50YXRpb24gaW4gZm91ciBFdXJvcGVhbiBwb3B1bGF0aW9ucywgKlBMb1MgT25lKjogPGh0dHBzOi8vam91cm5hbHMucGxvcy5vcmcvcGxvc29uZS9hcnRpY2xlP2lkPTEwLjEzNzEvam91cm5hbC5wb25lLjAwNDgyOTQ+LiBUb29rIGFsbCA4IFNOUHMgZnJvbSBUYWJsZSAyLgrigKIgJzIwMTAwNjE0X0dlcnN0ZW5ibGl0aC1ldC1hbCc6ICoqR2Vyc3RlbmJsaXRoIGV0IGFsLiAoMjAxMCkqKiBHZW5vbWUtd2lkZSBhc3NvY2lhdGlvbiBzdHVkaWVzIG9mIHBpZ21lbnRhdGlvbiBhbmQgc2tpbiBjYW5jZXI6IGEgcmV2aWV3IGFuZCBtZXRhLWFuYWx5c2lzLCAqUGlnbWVudCBDZWxsIE1lbGFub21hIFJlc2VhcmNoKjogPGh0dHBzOi8vb25saW5lbGlicmFyeS53aWxleS5jb20vZG9pL2Z1bGwvMTAuMTExMS9qLjE3NTUtMTQ4WC4yMDEwLjAwNzMwLng+LiBUb29rIGFsbCAzOSBTTlBzIGZyb20gVGFibGUgNC4K4oCiICcyMDA4MDUxNl9IYW4tZXQtYWwnOiAqKkhhbiBldCBhbC4gKDIwMDgpKiogQSBHZW5vbWUtV2lkZSBBc3NvY2lhdGlvbiBTdHVkeSBJZGVudGlmaWVzIE5vdmVsIEFsbGVsZXMgQXNzb2NpYXRlZCB3aXRoIEhhaXIgQ29sb3IgYW5kIFNraW4gUGlnbWVudGF0aW9uLCAqUExPUyBHZW5ldGljcyo6IDxodHRwczovL2pvdXJuYWxzLnBsb3Mub3JnL3Bsb3NnZW5ldGljcy9hcnRpY2xlP2lkPTEwLjEzNzEvam91cm5hbC5wZ2VuLjEwMDAwNzQ+LiBUb29rIGFsbCAzOCBTTlBzIGZyb20gVGFibGUgMi4gCgogIAojIyMgQ3JlYXRlIGxpc3Qgb2YgdGFyZ2V0IFNOUHMKICAKYGBge3J9CnBpZ19zbnBzIDwtIGxpc3QoKQojIENyYXdmb3JkCnBpZ19zbnBzW1siY3Jhd2ZvcmQiXV0gPC0gcmVhZHhsOjpyZWFkX2V4Y2VsKGhlcmUoImRhdGEiLCAiMjAxNzExMTdfQ3Jhd2ZvcmQtZXQtYWxfVGFibGUtMS54bHN4IikpICU+JSAKICBkcGx5cjo6c2VsZWN0KHJzaWQgPSAiUlNJRCIsIGV2ZXJ5dGhpbmcoKSkgJT4lIAogIGRwbHlyOjpmaWx0ZXIoIWlzLm5hKHJzaWQpLAogICAgICAgICAgICAgICAgIXJzaWQgPT0gIi4iKQoKIyBBZGhpa2FyaQpwaWdfc25wc1tbImFkaGlrYXJpX3RibF8xIl1dIDwtIHJlYWR4bDo6cmVhZF9leGNlbChoZXJlKCJkYXRhIiwgIjIwMTkwMTIxX0FkaGlrYXJpLWV0LWFsX3NucHMueGxzeCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaGVldCA9ICJUYWJsZSAxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2tpcCA9IDEpICU+JSAKICBkcGx5cjo6c2VsZWN0KHJzaWQgPSAicnNJRCIsIGV2ZXJ5dGhpbmcoKSkgJT4lIAogIGRwbHlyOjpmaWx0ZXIoIWlzLm5hKHJzaWQpKQoKcGlnX3NucHNbWyJhZGhpa2FyaV9zdXBwXzYiXV0gPC0gcmVhZHhsOjpyZWFkX2V4Y2VsKGhlcmUoImRhdGEiLCAiMjAxOTAxMjFfQWRoaWthcmktZXQtYWxfc25wcy54bHN4IiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNoZWV0ID0gInN1cHBfdGFibGVfNiIpICU+JSAKICBkcGx5cjo6c2VsZWN0KHJzaWQgPSAiU05QIiwgZXZlcnl0aGluZygpKSAlPiUgCiAgZHBseXI6OmZpbHRlcighaXMubmEocnNpZCkpICAKCnBpZ19zbnBzW1siYWRoaWthcmlfc3VwcF8xMiJdXSA8LSByZWFkeGw6OnJlYWRfZXhjZWwoaGVyZSgiZGF0YSIsICIyMDE5MDEyMV9BZGhpa2FyaS1ldC1hbF9zbnBzLnhsc3giKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hlZXQgPSAic3VwcF90YWJsZV8xMiIpICU+JSAKICBkcGx5cjo6c2VsZWN0KHJzaWQgPSAiU05QIiwgZXZlcnl0aGluZygpKSAlPiUgCiAgZHBseXI6OmZpbHRlcighaXMubmEocnNpZCkpCgojIEhlcm5hbmRlei1QYWNoZWNvCnBpZ19zbnBzW1siaGVybmFuZGV6LXBhY2hlY28iXV0gPC0gcmVhZHhsOjpyZWFkX2V4Y2VsKGhlcmUoImRhdGEiLCAiMjAxNzAzMTZfSGVybmFuZGV6LVBhY2hlY28tZXQtYWwueGxzeCIpKQoKIyBEb2Mgd2l0aCBTTlBzIGZyb20gbXVsdGlwbGUgc3R1ZGllcwpzaGVldF9uYW1lcyA8LSByZWFkeGw6OmV4Y2VsX3NoZWV0cyhoZXJlKCJkYXRhIiwgIjIwMjAwNjIyX3BpZ21lbnRhdGlvbl9zbnBzLnhsc3giKSkKY29tcGlsZWRfc25wcyA8LSBsYXBwbHkoc2hlZXRfbmFtZXMsIGZ1bmN0aW9uKHgpewogIHggPC0gcmVhZHhsOjpyZWFkX2V4Y2VsKGhlcmUoImRhdGEiLCAiMjAyMDA2MjJfcGlnbWVudGF0aW9uX3NucHMueGxzeCIpLAogICAgICAgICAgICAgICAgICAgICBzaGVldCA9IHgpCn0pCm5hbWVzKGNvbXBpbGVkX3NucHMpIDwtIHNoZWV0X25hbWVzCgojIENvbWJpbmUKcGlnX3NucHMgPC0gYyhwaWdfc25wcywgY29tcGlsZWRfc25wcykKYGBgCgojIyMgR2V0IHN1bW1hcnkgc3RhdHMKCmBgYHtyfQojIEhvdyBtYW55IHRvdGFsIFNOUHMKc3VtKHVubGlzdChsYXBwbHkocGlnX3NucHMsIG5yb3cpKSkKYGBgCgojIyMgUHVsbCBvdXQgdW5pcXVlIFNOUHMKCmBgYHtyfQpwaWdfZGYgPC0gbGFwcGx5KHBpZ19zbnBzLCBmdW5jdGlvbih4KSBkcGx5cjo6c2VsZWN0KHgsIHJzaWQpKQpwaWdfZGYgPC0gZHBseXI6OmJpbmRfcm93cyhwaWdfZGYpCnBpZ19kZiA8LSB1bmlxdWUocGlnX2RmKQoKbnJvdyhwaWdfZGYpCmBgYAoKIyMjIFdyaXRlIHRhYmxlCgpgYGB7cn0Kd3JpdGUudGFibGUoeCA9IHBpZ19kZiRyc2lkLAogICAgICAgICAgICBmaWxlID0gaGVyZTo6aGVyZSgiZGF0YSIsICIyMDIwMDYyMl9zbnBzX3BpZy5saXN0IiksCiAgICAgICAgICAgIHF1b3RlID0gRiwKICAgICAgICAgICAgcm93Lm5hbWVzID0gRiwKICAgICAgICAgICAgY29sLm5hbWVzID0gRikKYGBgCgojIyMgRXh0cmFjdCBjYWxscyBmb3IgdGhvc2UgU05QcyBmcm9tIFZDRgoKYGBge2Jhc2h9CmdhdGsgU2VsZWN0VmFyaWFudHMgXAogIC1SIHJlZnMvaHMzN2Q1LmZhLmd6IFwKICAtViB2Y2ZzLzFna19hbGwudmNmLmd6IFwKICAtLWtlZXAtaWRzIHJhY2lzdF9oeXBvdGhlc2lzL2RhdGEvMjAyMDA2MjJfc25wc19waWcubGlzdCBcCiAgLU8gdmNmcy9zbnBoaXRzX3BpZy52Y2YuZ3oKYGBgCgojIyMgQ29weSBiYWNrIHRvIGRhdGEgZm9sZGVyCgpgYGB7YmFzaCwgZXZhbCA9IEZ9CmNwIHZjZnMvc25waGl0c19waWcudmNmLmd6KiByYWNpc3RfaHlwb3RoZXNpcy9kYXRhLwpgYGAKCgojIyBHZXQgbWV0YWRhdGEKCmBgYHtyfQptZXRhIDwtIHJlYWRfeGxzeChoZXJlOjpoZXJlKCJkYXRhIiwgIjIwMTMwNjA2X3NhbXBsZV9pbmZvLnhsc3giKSwgc2hlZXQgPSAiU2FtcGxlIEluZm8iKSAlPiUgZHBseXI6OnNlbGVjdChTYW1wbGUsIFBvcHVsYXRpb24sIEdlbmRlcikKYGBgCgojIyMgV3JpdGUgZm9yIHBsaW5rIGZpbGUKCmBgYHtyfQp3cml0ZS50YWJsZShtZXRhWywgMToyXSwKICAgICAgICAgICAgaGVyZTo6aGVyZSgiZGF0YSIsICJwbGluazJfc2FtcGxlX3BvcG5fa2V5LnR4dCIpLAogICAgICAgICAgICBxdW90ZSA9IEYsCiAgICAgICAgICAgIHNlcCA9ICJcdCIsCiAgICAgICAgICAgIHJvdy5uYW1lcyA9IEYsCiAgICAgICAgICAgIGNvbC5uYW1lcyA9IEYpCmBgYAoKCiMjIFJlYWQgaW4gVkNGcyBhbmQgb2J0YWluIGFsbGVsZSBjb3VudHMgZm9yIHRhcmdldCBTTlBzCgpgYGB7cn0KIyBsaXN0IHRhcmdldCBWQ0ZzCnRhcmdldF92Y2ZzIDwtIGxpc3QuZmlsZXMoaGVyZTo6aGVyZSgiZGF0YSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSBnbG9iMnJ4KCJzbnBoaXRzXyouZ3oiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgZnVsbC5uYW1lcyA9IFQpCiMgcmVtb3ZlIHRoZSBzaG9ydGVyIElCRCBsaXN0CnRhcmdldF92Y2ZzIDwtIHRhcmdldF92Y2ZzW2MoMSwgMiwgNSldCgojIHJlYWQgaW4gVkNGcyBhbmQgZ2V0IGFsbGVsZSBjb3VudHMKdmNmX2xpc3QgPC0gbGFwcGx5KHRhcmdldF92Y2ZzLCBmdW5jdGlvbih2Y2ZfZmlsZSl7CiAgIyByZWFkIGluIFZDRnMKICB2Y2Zfb3V0IDwtIHBlZ2FzOjpyZWFkLnZjZih2Y2ZfZmlsZSkKICAjIGNyZWF0ZSBwb3B1bGF0aW9uIGNvbHVtbgogIHBvcHVsYXRpb25zIDwtIHVubGlzdChsYXBwbHkocm93bmFtZXModmNmX291dCksIGZ1bmN0aW9uKHNhbXBsZSl7CiAgICBtZXRhJFBvcHVsYXRpb25bbWV0YSRTYW1wbGUgPT0gc2FtcGxlXQogIH0pKQogIHZjZl9vdXQkcG9wdWxhdGlvbiA8LSBwb3B1bGF0aW9ucwogICMgcmVvcmRlcgogIHZjZl9vdXQgPC0gdmNmX291dCAlPiUgZHBseXI6OnNlbGVjdChwb3B1bGF0aW9uLCBldmVyeXRoaW5nKCkpCiAgIyBzcGxpdCBieSBwb3B1bGF0aW9uCiAgdmNmX291dF9zcGxpdCA8LSBzcGxpdCh2Y2Zfb3V0LCBmID0gdmNmX291dCRwb3B1bGF0aW9uKQogICMgcmVtb3ZlIHBvcHVsYXRpb24gY29sdW1ucwogIHZjZl9vdXRfc3BsaXQgPC0gbGFwcGx5KHZjZl9vdXRfc3BsaXQsIGZ1bmN0aW9uKHgpewogICAgeCRwb3B1bGF0aW9uIDwtIE5VTEwKICAgIHJldHVybih4KQogIH0pCiMgICMgZ2V0IGFsbGVsZSBjb3VudHMKIyAgYWxsZWxlX2NvdW50cyA8LSBsYXBwbHkodmNmX291dF9zcGxpdCwgZnVuY3Rpb24ocG9wdWxhdGlvbil7CiMgICAgc3VtbWFyeShwb3B1bGF0aW9uKQojICB9KQojICByZXR1cm4oYWxsZWxlX2NvdW50cykKfSkKCnRlc3QgPC0gbGFwcGx5KHZjZl9saXN0WzFdLCBmdW5jdGlvbih2Y2Zfb3V0KXsKICAjIGdldCBhbGxlbGUgY291bnRzCiAgYWxsZWxlX2NvdW50cyA8LSBsYXBwbHkodmNmX291dCwgZnVuY3Rpb24ocG9wdWxhdGlvbil7CiAgICBzdW1tYXJ5KHBvcHVsYXRpb24pCiAgfSkKICByZXR1cm4oYWxsZWxlX2NvdW50cykKfSkKCnRlc3QgPC0gbGFwcGx5KHZjZl9saXN0LCBmdW5jdGlvbih2Y2Zfb3V0KXsKICAjIGNyZWF0ZSBwb3B1bGF0aW9uIGNvbHVtbgogIHBvcHVsYXRpb25zIDwtIHVubGlzdChsYXBwbHkocm93bmFtZXModmNmX291dCksIGZ1bmN0aW9uKHNhbXBsZSl7CiAgICBtZXRhJFBvcHVsYXRpb25bbWV0YSRTYW1wbGUgPT0gc2FtcGxlXQogIH0pKQp9KQoKIyBzZXQgbmFtZXMKbmFtZXModmNmX2xpc3QpIDwtIGdzdWIoInNucGhpdHNffC52Y2YuZ3oiLCAiIiwgbGlzdC5maWxlcyhoZXJlOjpoZXJlKCJkYXRhIiksIHBhdHRlcm4gPSBnbG9iMnJ4KCJzbnBoaXRzXyouZ3oiKSlbMTozXSkKYGBgCgojIyBOb3Qgd29ya2luZyB3ZWxsLiBUcnkgYHZjZlJgCgpgYGB7cn0KdGVzdCA8LSB2Y2ZSOjpyZWFkLnZjZlIodGFyZ2V0X3ZjZnNbMV0pCgpgYGAKCiMjIFRyeSB3aXRoIFBsaW5rCgojIyMgTWFrZSBwb3B1bGF0aW9uIGZpbGUKCmBgYHtiYXNoLCBldmFsID0gRn0KL25mcy9zb2Z0d2FyZS9iaXJuZXkvcGxpbmsyIC0tdmNmIHZjZnMvc25waGl0c19waWcudmNmLmd6CmBgYAoKIyMjIFNldCB1cCBkaXJlY3RvcmllcwoKYGBge2Jhc2gsIGV2YWwgPSBGfQpta2RpciByYWNpc3RfaHlwb3RoZXNpcy9kYXRhLzIwMjAwNjIyX3BsaW5rMl9hbGZyZXFzCm1rZGlyIHJhY2lzdF9oeXBvdGhlc2lzL2RhdGEvMjAyMDA2MjJfcGxpbmsyX2FsZnJlcXMvaGVpCm1rZGlyIHJhY2lzdF9oeXBvdGhlc2lzL2RhdGEvMjAyMDA2MjJfcGxpbmsyX2FsZnJlcXMvZWR1Cm1rZGlyIHJhY2lzdF9oeXBvdGhlc2lzL2RhdGEvMjAyMDA2MjJfcGxpbmsyX2FsZnJlcXMvcGlnCmBgYAoKIyMjIFJ1biBwbGluawoKYGBge2Jhc2h9CiMgRWR1IFllYXJzCi9uZnMvc29mdHdhcmUvYmlybmV5L3BsaW5rMi4zL3BsaW5rMiBcCiAgLS12Y2YgdmNmcy9zbnBoaXRzX2VkdXlycy52Y2YuZ3ogXAogIC0tZnJlcSBcCiAgLS1tYXgtYWxsZWxlcyAyIFwKICAtLXBoZW5vIGlpZC1vbmx5IHJhY2lzdF9oeXBvdGhlc2lzL2RhdGEvcGxpbmsyX3NhbXBsZV9wb3BuX2tleS50eHQgXAogIC0tbG9vcC1jYXRzIFBIRU5PMSBcCiAgLS1vdXQgcmFjaXN0X2h5cG90aGVzaXMvZGF0YS8yMDIwMDYyMl9wbGluazJfYWxmcmVxcy9lZHUvZWR1CgojIEhlaWdodAovbmZzL3NvZnR3YXJlL2Jpcm5leS9wbGluazIuMy9wbGluazIgXAogIC0tdmNmIHZjZnMvc25waGl0c19oZWlnaHQudmNmLmd6IFwKICAtLWZyZXEgXAogIC0tbWF4LWFsbGVsZXMgMiBcCiAgLS1waGVubyBpaWQtb25seSByYWNpc3RfaHlwb3RoZXNpcy9kYXRhL3BsaW5rMl9zYW1wbGVfcG9wbl9rZXkudHh0IFwKICAtLWxvb3AtY2F0cyBQSEVOTzEgXAogIC0tb3V0IHJhY2lzdF9oeXBvdGhlc2lzL2RhdGEvMjAyMDA2MjJfcGxpbmsyX2FsZnJlcXMvaGVpL2hlaQoKIyBQaWdtZW50YXRpb24KL25mcy9zb2Z0d2FyZS9iaXJuZXkvcGxpbmsyLjMvcGxpbmsyIFwKICAtLXZjZiB2Y2ZzL3NucGhpdHNfcGlnLnZjZi5neiBcCiAgLS1mcmVxIFwKICAtLW1heC1hbGxlbGVzIDIgXAogIC0tcGhlbm8gaWlkLW9ubHkgcmFjaXN0X2h5cG90aGVzaXMvZGF0YS9wbGluazJfc2FtcGxlX3BvcG5fa2V5LnR4dCBcCiAgLS1sb29wLWNhdHMgUEhFTk8xIFwKICAtLW91dCByYWNpc3RfaHlwb3RoZXNpcy9kYXRhLzIwMjAwNjIyX3BsaW5rMl9hbGZyZXFzL3BpZy9waWcKYGBgCgojIyMgQ29tbWl0CgpgYGB7YmFzaCwgZXZhbCA9IEZ9CmdpdCBjb21taXRhbGwgIjIwMjAwNjIyIHBsaW5rIGRhdGEiCmBgYAoKIyMjIFJlYWQgaW4gZGF0YQoKYGBge3J9CnRhcmdldF9kaXJzIDwtIGxpc3QuZGlycyhoZXJlOjpoZXJlKCJkYXRhIiwgIjIwMjAwNjIyX3BsaW5rMl9hbGZyZXFzIiksIHJlY3Vyc2l2ZSA9IEYpCgphbF9mcmVxX2xzdCA8LSBsYXBwbHkodGFyZ2V0X2RpcnMsIGZ1bmN0aW9uKHgpewogIHRhcmdldF9maWxlcyA8LSBsaXN0LmZpbGVzKHgsIHBhdHRlcm4gPSAiLmFmcmVxIiwgZnVsbC5uYW1lcyA9IFQpCiAgIyByZWFkIGluIGRhdGEKICBkYXRhX2xzdCA8LSBsYXBwbHkodGFyZ2V0X2ZpbGVzLCBmdW5jdGlvbih0YXJnZXRfZmlsZSl7CiAgICByZWFkLnRhYmxlKHRhcmdldF9maWxlLAogICAgICAgICAgICAgICBoZWFkZXIgPSBULAogICAgICAgICAgICAgICBjb21tZW50LmNoYXIgPSAiIikKICB9KQogICMgZml4IG5hbWVzIG9mIHBvcHVsYXRpb25zCiAgbmFtZXMoZGF0YV9sc3QpIDwtIGdzdWIocGF0dGVybiA9ICJlZHUufGhlaS58cGlnLnwuYWZyZXEiLAogICAgICAgICAgICAgICAgICAgICAgICAgIHJlcGxhY2VtZW50ID0gIiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IGxpc3QuZmlsZXMoeCwgcGF0dGVybiA9ICIuYWZyZXEiKSkKICByZXR1cm4oZGF0YV9sc3QpCn0pCgojIHNldCBuYW1lcwpuYW1lcyhhbF9mcmVxX2xzdCkgPC0gYmFzZW5hbWUodGFyZ2V0X2RpcnMpCmBgYAoKIyMjIFR1cm4gaW50byBzaW5nbGUgdGFibGUgZm9yIGVhY2ggcGhlbm8KCmBgYHtyfQphbF9mcmVxX2RmIDwtIGxhcHBseShhbF9mcmVxX2xzdCwgZnVuY3Rpb24ocGhlbm8pewogIG91dCA8LSBkcGx5cjo6YmluZF9yb3dzKHBoZW5vLCAuaWQgPSAicG9wdWxhdGlvbiIpICU+JSAKICAgIHRpZHlyOjpwaXZvdF93aWRlcihpZF9jb2xzID0gYyhYLkNIUk9NLCBJRCwgUkVGLCBBTFQpLAogICAgICAgICAgICAgICAgICAgICAgIG5hbWVzX2Zyb20gPSBwb3B1bGF0aW9uLAogICAgICAgICAgICAgICAgICAgICAgIHZhbHVlc19mcm9tID0gQUxUX0ZSRVFTKQp9KQoKCiNhbF9mcmVxX2RmIDwtIGRwbHlyOjpiaW5kX3Jvd3MoYWxfZnJlcV9kZiwgLmlkID0gInBoZW5vdHlwZSIpCmBgYAoKIyMgU2V0IHVwIHZlY3RvcnMgZm9yIHBoZW5vdHlwZS1zcGVjaWZpYyBwYXJhbWV0ZXJzCgpgYGB7cn0KIyBUaXRsZXMKdGl0bGVzIDwtIGMoIkVkdWNhdGlvbmFsIEF0dGFpbm1lbnQiLCAiSGVpZ2h0IiwgIlBpZ21lbnRhdGlvbiIpCmBgYAoKIyMgUGxvdAoKIyMjIFlSSSB2IENFVQoKYGBge3J9CmNvdW50ZXIgPC0gMApsYXBwbHkoYWxfZnJlcV9kZiwgZnVuY3Rpb24ocGhlbm8pewogIGNvdW50ZXIgPDwtIGNvdW50ZXIgKyAxCiAgZ2dwbG90KHBoZW5vLAogICAgICAgICBhZXMoWVJJLCBDRVUpKSArCiAgICBnZW9tX3BvaW50KCkgKwojICAgIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYyhvcHRpb24gPSBjb2xvdXJfcGFsc1tjb3VudGVyXSkgKwogICAgY29vcmRfZml4ZWQoKSArCiAgICBnZW9tX3Ntb290aChzZSA9IEYsIGNvbG91ciA9ICJyZWQiKSArCiAgICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAwLCBzbG9wZSA9IDEsIGNvbG91ciA9ICJibHVlIikgKwogICAgeGxhYigiQWxsZWxlIGZyZXF1ZW5jeSBpbiBZUkkiKSArCiAgICB5bGFiKCJBbGxlbGUgZnJlcXVlbmN5IGluIENFVSIpICsKICAgIGxhYnModGl0bGUgPSB0aXRsZXNbY291bnRlcl0pCn0pCmBgYAojIyMgWVJJIHYgQ0hTCgpgYGB7cn0KY291bnRlciA8LSAwCmxhcHBseShhbF9mcmVxX2RmLCBmdW5jdGlvbihwaGVubyl7CiAgY291bnRlciA8PC0gY291bnRlciArIDEKICBnZ3Bsb3QocGhlbm8sCiAgICAgICAgIGFlcyhZUkksIENIUykpICsKICAgIGdlb21fcG9pbnQoKSArCiMgICAgc2NhbGVfY29sb3JfdmlyaWRpc19jKG9wdGlvbiA9IGNvbG91cl9wYWxzW2NvdW50ZXJdKSArCiAgICBjb29yZF9maXhlZCgpICsKICAgIGdlb21fc21vb3RoKHNlID0gRiwgY29sb3VyID0gInJlZCIpICsKICAgIGdlb21fYWJsaW5lKGludGVyY2VwdCA9IDAsIHNsb3BlID0gMSwgY29sb3VyID0gImJsdWUiKSArCiAgICB4bGFiKCJBbGxlbGUgZnJlcXVlbmN5IGluIFlSSSIpICsKICAgIHlsYWIoIkFsbGVsZSBmcmVxdWVuY3kgaW4gQ0hTIikgKwogICAgbGFicyh0aXRsZSA9IHRpdGxlc1tjb3VudGVyXSkKfSkKYGBgCiMjIyBEbyBhZ2FpbiBhZnRlciByYW5kb21seSBzd2FwcGluZyBtaW5vciBhbGxlbGUKCmBgYHtyfQpzZXQuc2VlZCg2NSkKcmRtX3NkcyA8LSBzYW1wbGUoMToxMDAsIDMpCgpjb3VudGVyIDwtIDAKYWxfZnJlcV9kZl9zaHVmZiA8LSBsYXBwbHkoYWxfZnJlcV9kZiwgZnVuY3Rpb24ocGhlbm8pewogIGNvdW50ZXIgPDwtIGNvdW50ZXIgKyAxCiAgIyBzZXQgc2VlZAogIHNldC5zZWVkKHJkbV9zZHNbY291bnRlcl0pCiAgIyBzZWxlY3QgU05QcyB0byBzd2FwIChoYWxmIG9mIHRvdGFsKQogIHRndF9pbmRjcyA8LSBzYW1wbGUobnJvdyhwaGVubyksIG5yb3cocGhlbm8pIC8yKQogICMgc3dhcCBtaW5vciBhbGxlbGVzCiAgcGhlbm9bdGd0X2luZGNzLCA1Om5jb2wocGhlbm8pXSA8LSAxIC0gcGhlbm9bdGd0X2luZGNzLCA1Om5jb2wocGhlbm8pXQogICMgcmV0dXJuIHBoZW5vCiAgcmV0dXJuKHBoZW5vKQp9KQpgYGAKCmBgYHtyfQpjb3VudGVyIDwtIDAKbGFwcGx5KGFsX2ZyZXFfZGZfc2h1ZmYsIGZ1bmN0aW9uKHBoZW5vKXsKICBjb3VudGVyIDw8LSBjb3VudGVyICsgMQogIGdncGxvdChwaGVubywKICAgICAgICAgYWVzKFlSSSwgQ0hTKSkgKwogICAgZ2VvbV9wb2ludCgpICsKIyAgICBzY2FsZV9jb2xvcl92aXJpZGlzX2Mob3B0aW9uID0gY29sb3VyX3BhbHNbY291bnRlcl0pICsKICAgIGNvb3JkX2ZpeGVkKCkgKwogICAgZ2VvbV9zbW9vdGgoc2UgPSBGLCBjb2xvdXIgPSAicmVkIikgKwogICAgZ2VvbV9hYmxpbmUoaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSAxLCBjb2xvdXIgPSAiYmx1ZSIpICsKICAgIHhsYWIoIkFsbGVsZSBmcmVxdWVuY3kgaW4gWVJJIikgKwogICAgeWxhYigiQWxsZWxlIGZyZXF1ZW5jeSBpbiBDSFMiKSArCiAgICBsYWJzKHRpdGxlID0gdGl0bGVzW2NvdW50ZXJdKQp9KQpgYGAKCiMjIEZzdAoKYGBge3J9CiMgbGlzdCB0YXJnZXQgVkNGcwp0YXJnZXRfdmNmcyA8LSBsaXN0LmZpbGVzKGhlcmU6OmhlcmUoImRhdGEiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gZ2xvYjJyeCgic25waGl0c18qLmd6IiksIAogICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bGwubmFtZXMgPSBUKQoKIyBmaWx0ZXIgZm9yIHRoZSB0aHJlZSB3ZSB3YW50CnRhcmdldF92Y2ZzIDwtIHRhcmdldF92Y2ZzW2dyZXAoImVkdXlyc3xoZWlnaHR8cGlnIiwgdGFyZ2V0X3ZjZnMpXQoKCmBgYAoKIyMjIENyZWF0ZSBkYXRhIGZyYW1lcwoKYGBge3J9CiMgQ3JlYXRlIHJhdyBsaXN0IG9mIHZhcmlhbnRzCnZjZl9saXN0X3JhdyA8LSBsYXBwbHkodGFyZ2V0X3ZjZnMsIGZ1bmN0aW9uKHZjZl9maWxlKXsKICB2Y2Zfb3V0IDwtIHBlZ2FzOjpyZWFkLnZjZih2Y2ZfZmlsZSkKfSkKCiMgQ3JlYXRlIHZlY3RvciBvZiBwb3B1bGF0aW9ucwpwb3B1bGF0aW9ucyA8LSB1bmxpc3QobGFwcGx5KHJvd25hbWVzKHZjZl9saXN0X3Jhd1tbMV1dKSwgZnVuY3Rpb24oc2FtcGxlKXsKICBtZXRhJFBvcHVsYXRpb25bbWV0YSRTYW1wbGUgPT0gc2FtcGxlXQp9KSkKCiMgR2VuZXJhdGUgRnN0IHN0YXRzCmZzdF9vdXRfbHN0IDwtIGxhcHBseSh2Y2ZfbGlzdF9yYXcsIGZ1bmN0aW9uKHBoZW5vKXsKICBhcy5kYXRhLmZyYW1lKHBlZ2FzOjpGc3QocGhlbm8sIHBvcCA9IHBvcHVsYXRpb25zKSkKfSkKCiMgbWFrZSByb3duYW1lcyBpbnRvIHNlcGFyYXRlIGNvbHVtbgpmc3Rfb3V0X2xzdCA8LSBsYXBwbHkoZnN0X291dF9sc3QsIGZ1bmN0aW9uKHBoZW5vKXsKICBwaGVubyRzbnAgPC0gcm93bmFtZXMocGhlbm8pCiAgcmV0dXJuKHBoZW5vKQp9KQpuYW1lcyhmc3Rfb3V0X2xzdCkgPC0gdGl0bGVzCgojIGJpbmQgaW50byBzaW5nbGUgREYKZnN0X291dF9kZiA8LSBkcGx5cjo6YmluZF9yb3dzKGZzdF9vdXRfbHN0LCAuaWQgPSAicGhlbm90eXBlIikKaGVhZChmc3Rfb3V0X2RmKQpgYGAKCiMjIFBsb3QgZGVuc2l0eQoKYGBge3J9CmdncGxvdChmc3Rfb3V0X2RmLCBhZXMoRnN0LCBmaWxsID0gcGhlbm90eXBlKSkgKwogIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuNykgKwogIGxhYnMoZmlsbCA9ICJQaGVub3R5cGUiKSArCiAgeWxhYigiRGVuc2l0eSIpICsKICB0aGVtZV9idygpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCIjMDBBRkJCIiwgIiNFN0I4MDAiLCAiI0ZDNEUwNyIpKQpgYGAKYGBge3J9CmdncGxvdCgpICsKICBnZW9tX2RlbnNpdHlfcmlkZ2VzMihkYXRhID0gZnN0X291dF9kZiwKICAgICAgICAgICAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBGc3QsIHkgPSBwaGVub3R5cGUsIGZpbGwgPSBwaGVub3R5cGUpKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiIzAwQUZCQiIsICIjRTdCODAwIiwgIiNGQzRFMDciKSkgKwogIHlsYWIobGFiZWwgPSBOVUxMKSArCiAgdGhlbWVfYncoKQpgYGAKCgojIyMgVHJ5IHdpdGgganVzdCBZUkksIENFVSwgYW5kIENIUwoKYGBge3J9CiMgZ2V0IHNhbXBsZXMgZnJvbSB0YXJnZXQgcG9wbnMgb25seQp0YXJnZXRfcG9wbnMgPC0gd2hpY2gocG9wdWxhdGlvbnMgJWluJSBjKCJZUkkiLCAiQ0VVIiwgIkNIUyIpKQpwb3B1bGF0aW9uc18zcG9wIDwtIHBvcHVsYXRpb25zW3RhcmdldF9wb3Buc10KCnZjZl9saXN0X3Jhd18zcG9wIDwtIGxhcHBseSh2Y2ZfbGlzdF9yYXcsIGZ1bmN0aW9uKHBoZW5vKXsKICBwaGVub1t0YXJnZXRfcG9wbnMsIF0KfSkKCiMgR2VuZXJhdGUgRnN0IHN0YXRzCmZzdF9vdXRfbHN0XzNwb3AgPC0gbGFwcGx5KHZjZl9saXN0X3Jhd18zcG9wLCBmdW5jdGlvbihwaGVubyl7CiAgYXMuZGF0YS5mcmFtZShwZWdhczo6RnN0KHBoZW5vLCBwb3AgPSBwb3B1bGF0aW9uc18zcG9wKSkKfSkKCiMgbWFrZSByb3duYW1lcyBpbnRvIHNlcGFyYXRlIGNvbHVtbgpmc3Rfb3V0X2xzdF8zcG9wIDwtIGxhcHBseShmc3Rfb3V0X2xzdF8zcG9wLCBmdW5jdGlvbihwaGVubyl7CiAgcGhlbm8kc25wIDwtIHJvd25hbWVzKHBoZW5vKQogIHJldHVybihwaGVubykKfSkKbmFtZXMoZnN0X291dF9sc3RfM3BvcCkgPC0gdGl0bGVzCgojIGJpbmQgaW50byBzaW5nbGUgREYKZnN0X291dF9kZl8zcG9wIDwtIGRwbHlyOjpiaW5kX3Jvd3MoZnN0X291dF9sc3RfM3BvcCwgLmlkID0gInBoZW5vdHlwZSIpCmhlYWQoZnN0X291dF9kZl8zcG9wKQpgYGAKCiMjIyBQbG90CgpgYGB7cn0KZ2dwbG90KGZzdF9vdXRfZGZfM3BvcCwgYWVzKEZzdCwgZmlsbCA9IHBoZW5vdHlwZSkpICsKICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjcpICsKICBsYWJzKGZpbGwgPSAiUGhlbm90eXBlIikgKwogIHlsYWIoIkRlbnNpdHkiKSArCiAgdGhlbWVfYncoKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiIzAwQUZCQiIsICIjRTdCODAwIiwgIiNGQzRFMDciKSkKYGBgCgoKCiMjIyBUcnkgd2l0aCByaWRnZXMKCmBgYHtyfQojIGZhY3RvcmlzZSAKZnN0X291dF9kZl8zcG9wJHBoZW5vdHlwZSA8LSBmYWN0b3IoZnN0X291dF9kZl8zcG9wJHBoZW5vdHlwZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiUGlnbWVudGF0aW9uIiwgIkhlaWdodCIsICJFZHVjYXRpb25hbCBBdHRhaW5tZW50IikpCgpnZ3Bsb3QoKSArCiAgZ2VvbV9kZW5zaXR5X3JpZGdlczIoZGF0YSA9IGZzdF9vdXRfZGZfM3BvcCwKICAgICAgICAgICAgICAgICAgICAgICBtYXBwaW5nID0gYWVzKHggPSBGc3QsIHkgPSBwaGVub3R5cGUsIGZpbGwgPSBwaGVub3R5cGUpLAogICAgICAgICAgICAgICAgICAgICAgIHNjYWxlID0gMC41LAogICAgICAgICAgICAgICAgICAgICAgIGFscGhhID0gMC44KSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiI0ZDNEUwNyIsICIjMDBBRkJCIiwgIiNFN0I4MDAiKSkgKwogIHlsYWIobGFiZWwgPSBOVUxMKSArCiAgdGhlbWVfYncoKSAjKwogIHRoZW1lX3JpZGdlcyhjZW50ZXIgPSBUKQpgYGAKYGBge3J9CnBsb3RfbHkoeCA9IGFsX2ZyZXFfZGYkZWR1JENFVSwKICAgICAgICB5ID0gYWxfZnJlcV9kZiRlZHUkWVJJLAogICAgICAgIHogPSBhbF9mcmVxX2RmJGVkdSRDSFMpICU+JSAKICBhZGRfbWFya2VycygpCmBgYAoKYGBge3J9CiMgZ2V0IGxtIGZvciBkYXRhCmhlaWdodF9sbSA8LSBsbShDSFMgfiAwICsgWVJJICsgQ0VVLCBkYXRhID0gYWxfZnJlcV9kZiRoZWkpCgpncmFwaF9yZXNvIDwtIDAuMDUKCiMgc2V0IHVwIGF4ZXMKYXhpc194IDwtIHNlcShtaW4oYWxfZnJlcV9kZiRoZWkkWVJJKSwgbWF4KGFsX2ZyZXFfZGYkaGVpJFlSSSksIGJ5ID0gZ3JhcGhfcmVzbykKYXhpc195IDwtIHNlcShtaW4oYWxfZnJlcV9kZiRoZWkkQ0VVKSwgbWF4KGFsX2ZyZXFfZGYkaGVpJENFVSksIGJ5ID0gZ3JhcGhfcmVzbykKCiMgc2FtcGxlIHBvaW50cwpoZWlnaHRfbG1fc3VyZmFjZSA8LSBleHBhbmQuZ3JpZChZUkkgPSBheGlzX3gsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENFVSA9IGF4aXNfeSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgS0VFUC5PVVQuQVRUUlMgPSBGKQpoZWlnaHRfbG1fc3VyZmFjZSRDSFMgPC0gcHJlZGljdC5sbShoZWlnaHRfbG0sIG5ld2RhdGEgPSBoZWlnaHRfbG1fc3VyZmFjZSkKaGVpZ2h0X2xtX3N1cmZhY2VfY2FzdCA8LSByZXNoYXBlMjo6YWNhc3QoaGVpZ2h0X2xtX3N1cmZhY2UsIENFVSB+IFlSSSwgdmFsdWUudmFyID0gIkNIUyIpCmBgYAoKIyMjIFBsb3QgaGVpZ2h0CgpXaXRoIGFzc2lzdGFuY2UgZnJvbSBoZXJlOiA8aHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvMzgzMzExOTgvYWRkLXJlZ3Jlc3Npb24tcGxhbmUtdG8tM2Qtc2NhdHRlci1wbG90LWluLXBsb3RseT4uCgpgYGB7cn0KIyBwbG90CmhlaWdodF9wbG90IDwtIHBsb3RfbHkoYWxfZnJlcV9kZiRoZWksCiAgICAgICAgICAgICAgICAgICAgICAgeCA9IH5DRVUsCiAgICAgICAgICAgICAgICAgICAgICAgeSA9IH5ZUkksCiAgICAgICAgICAgICAgICAgICAgICAgeiA9IH5DSFMsCiAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJzY2F0dGVyM2QiLAogICAgICAgICAgICAgICAgICAgICAgIG1vZGUgPSAibWFya2VycyIsCiAgICAgICAgICAgICAgICAgICAgICAgbWFya2VyID0gbGlzdChzaXplID0gMikpCiMgYWRkIHN1cmZhY2UKaGVpZ2h0X3Bsb3QgPC0gYWRkX3RyYWNlKHAgPSBoZWlnaHRfcGxvdCwKICAgICAgICAgICAgICAgICAgICAgICAgIHogPSBoZWlnaHRfbG1fc3VyZmFjZSwKICAgICAgICAgICAgICAgICAgICAgICAgIHggPSBheGlzX3gsCiAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gYXhpc195LAogICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICJzdXJmYWNlIikKCmhlaWdodF9wbG90CmBgYApgYGB7cn0KIyBnZXQgbG0gZm9yIGRhdGEKaGVpZ2h0X2xvZXNzIDwtIGxvZXNzKENIUyB+IDAgKyBZUkkgKyBDRVUsIGRhdGEgPSBhbF9mcmVxX2RmJGhlaSkKCmdyYXBoX3Jlc28gPC0gMC4wNQoKIyBzZXQgdXAgYXhlcwpheGlzX3ggPC0gc2VxKG1pbihhbF9mcmVxX2RmJGhlaSRZUkkpLCBtYXgoYWxfZnJlcV9kZiRoZWkkWVJJKSwgYnkgPSBncmFwaF9yZXNvKQpheGlzX3kgPC0gc2VxKG1pbihhbF9mcmVxX2RmJGhlaSRDRVUpLCBtYXgoYWxfZnJlcV9kZiRoZWkkQ0VVKSwgYnkgPSBncmFwaF9yZXNvKQoKIyBzYW1wbGUgcG9pbnRzCmhlaWdodF9sbV9zdXJmYWNlIDwtIGV4cGFuZC5ncmlkKFlSSSA9IGF4aXNfeCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ0VVID0gYXhpc195LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBLRUVQLk9VVC5BVFRSUyA9IEYpCmhlaWdodF9sbV9zdXJmYWNlJENIUyA8LSBwcmVkaWN0KGhlaWdodF9sb2VzcywgbmV3ZGF0YSA9IGhlaWdodF9sbV9zdXJmYWNlKQpoZWlnaHRfbG1fc3VyZmFjZSA8LSBhY2FzdChoZWlnaHRfbG1fc3VyZmFjZSwgQ0VVIH4gWVJJLCB2YWx1ZS52YXIgPSAiQ0hTIikKCmBgYAoKYGBge3J9CiMgcGxvdApoZWlnaHRfcGxvdCA8LSBwbG90X2x5KGFsX2ZyZXFfZGYkaGVpLAogICAgICAgICAgICAgICAgICAgICAgIHggPSB+Q0VVLAogICAgICAgICAgICAgICAgICAgICAgIHkgPSB+WVJJLAogICAgICAgICAgICAgICAgICAgICAgIHogPSB+Q0hTLAogICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAic2NhdHRlcjNkIiwKICAgICAgICAgICAgICAgICAgICAgICBtb2RlID0gIm1hcmtlcnMiLAogICAgICAgICAgICAgICAgICAgICAgIG1hcmtlciA9IGxpc3Qoc2l6ZSA9IDIpKQojIGFkZCBzdXJmYWNlCmhlaWdodF9wbG90IDwtIGFkZF90cmFjZShwID0gaGVpZ2h0X3Bsb3QsCiAgICAgICAgICAgICAgICAgICAgICAgICB6ID0gaGVpZ2h0X2xtX3N1cmZhY2UsCiAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gYXhpc194LAogICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGF4aXNfeSwKICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAic3VyZmFjZSIpCgpoZWlnaHRfcGxvdApgYGAKCiMjIERvIGFnYWluIHdpdGggTUFGIHNodWZmbGVkIGRhdGEKYGBge3J9CiMgZ2V0IGxtIGZvciBkYXRhCmhlaWdodF9sb2VzcyA8LSBsb2VzcyhDSFMgfiAwICsgWVJJICsgQ0VVLCBkYXRhID0gYWxfZnJlcV9kZl9zaHVmZiRoZWkpCgpncmFwaF9yZXNvIDwtIDAuMDUKCiMgc2V0IHVwIGF4ZXMKYXhpc194IDwtIHNlcShtaW4oYWxfZnJlcV9kZl9zaHVmZiRoZWkkWVJJKSwgbWF4KGFsX2ZyZXFfZGZfc2h1ZmYkaGVpJFlSSSksIGJ5ID0gZ3JhcGhfcmVzbykKYXhpc195IDwtIHNlcShtaW4oYWxfZnJlcV9kZl9zaHVmZiRoZWkkQ0VVKSwgbWF4KGFsX2ZyZXFfZGZfc2h1ZmYkaGVpJENFVSksIGJ5ID0gZ3JhcGhfcmVzbykKCiMgc2FtcGxlIHBvaW50cwpoZWlnaHRfbG1fc3VyZmFjZSA8LSBleHBhbmQuZ3JpZChZUkkgPSBheGlzX3gsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENFVSA9IGF4aXNfeSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgS0VFUC5PVVQuQVRUUlMgPSBGKQpoZWlnaHRfbG1fc3VyZmFjZSRDSFMgPC0gcHJlZGljdChoZWlnaHRfbG9lc3MsIG5ld2RhdGEgPSBoZWlnaHRfbG1fc3VyZmFjZSkKaGVpZ2h0X2xtX3N1cmZhY2UgPC0gYWNhc3QoaGVpZ2h0X2xtX3N1cmZhY2UsIENFVSB+IFlSSSwgdmFsdWUudmFyID0gIkNIUyIpCgpgYGAKCmBgYHtyfQojIHBsb3QKaGVpZ2h0X3Bsb3QgPC0gcGxvdF9seShhbF9mcmVxX2RmX3NodWZmJGhlaSwKICAgICAgICAgICAgICAgICAgICAgICB4ID0gfkNFVSwKICAgICAgICAgICAgICAgICAgICAgICB5ID0gfllSSSwKICAgICAgICAgICAgICAgICAgICAgICB6ID0gfkNIUywKICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInNjYXR0ZXIzZCIsCiAgICAgICAgICAgICAgICAgICAgICAgbW9kZSA9ICJtYXJrZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICBtYXJrZXIgPSBsaXN0KHNpemUgPSAyKSkKIyBhZGQgc3VyZmFjZQpoZWlnaHRfcGxvdCA8LSBhZGRfdHJhY2UocCA9IGhlaWdodF9wbG90LAogICAgICAgICAgICAgICAgICAgICAgICAgeiA9IGhlaWdodF9sbV9zdXJmYWNlLAogICAgICAgICAgICAgICAgICAgICAgICAgeCA9IGF4aXNfeCwKICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBheGlzX3ksCiAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInN1cmZhY2UiKQoKaGVpZ2h0X3Bsb3QKYGBgCiMjIERvIGFnYWluIHdpdGggTUFGIHNodWZmbGVkIGRhdGEKYGBge3J9CiMgZ2V0IGxtIGZvciBkYXRhCmhlaWdodF9sb2VzcyA8LSBsb2VzcyhZUkkgfiAwICsgQ0VVICsgQ0hTLCBkYXRhID0gYWxfZnJlcV9kZl9zaHVmZiRoZWkpCgpncmFwaF9yZXNvIDwtIDAuMDUKCiMgc2V0IHVwIGF4ZXMKYXhpc194IDwtIHNlcShtaW4oYWxfZnJlcV9kZl9zaHVmZiRoZWkkQ0VVKSwgbWF4KGFsX2ZyZXFfZGZfc2h1ZmYkaGVpJENFVSksIGJ5ID0gZ3JhcGhfcmVzbykKYXhpc195IDwtIHNlcShtaW4oYWxfZnJlcV9kZl9zaHVmZiRoZWkkQ0hTKSwgbWF4KGFsX2ZyZXFfZGZfc2h1ZmYkaGVpJENIUyksIGJ5ID0gZ3JhcGhfcmVzbykKCiMgc2FtcGxlIHBvaW50cwpoZWlnaHRfbG1fc3VyZmFjZSA8LSBleHBhbmQuZ3JpZChDRVUgPSBheGlzX3gsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENIUyA9IGF4aXNfeSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgS0VFUC5PVVQuQVRUUlMgPSBGKQpoZWlnaHRfbG1fc3VyZmFjZSRZUkkgPC0gcHJlZGljdChoZWlnaHRfbG9lc3MsIG5ld2RhdGEgPSBoZWlnaHRfbG1fc3VyZmFjZSkKaGVpZ2h0X2xtX3N1cmZhY2UgPC0gYWNhc3QoaGVpZ2h0X2xtX3N1cmZhY2UsIENIUyB+IENFVSwgdmFsdWUudmFyID0gIllSSSIpCgpgYGAKCmBgYHtyfQojIHBsb3QKaGVpZ2h0X3Bsb3QgPC0gcGxvdF9seShhbF9mcmVxX2RmX3NodWZmJGhlaSwKICAgICAgICAgICAgICAgICAgICAgICB4ID0gfkNFVSwKICAgICAgICAgICAgICAgICAgICAgICB5ID0gfkNIUywKICAgICAgICAgICAgICAgICAgICAgICB6ID0gfllSSSwKICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInNjYXR0ZXIzZCIsCiAgICAgICAgICAgICAgICAgICAgICAgbW9kZSA9ICJtYXJrZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICBtYXJrZXIgPSBsaXN0KHNpemUgPSAyKSkKIyBhZGQgc3VyZmFjZQpoZWlnaHRfcGxvdCA8LSBhZGRfdHJhY2UocCA9IGhlaWdodF9wbG90LAogICAgICAgICAgICAgICAgICAgICAgICAgeiA9IGhlaWdodF9sbV9zdXJmYWNlLAogICAgICAgICAgICAgICAgICAgICAgICAgeCA9IGF4aXNfeCwKICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBheGlzX3ksCiAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlID0gInN1cmZhY2UiKQoKaGVpZ2h0X3Bsb3QKYGBgCgojIyBEbyBmb3IgYWxsCgpgYGB7cn0KbGFwcGx5KGFsX2ZyZXFfZGZfc2h1ZmYsIGZ1bmN0aW9uKHBoZW5vKXsKICAjIHNldCBncmFwaCByZXNvbHV0aW9uCiAgZ3JhcGhfcmVzbyA8LSAwLjA1CiAgIyBnZXQgbG0gZm9yIGRhdGEKICBsb2Vzc19tb2RlbCA8LSBsb2VzcyhZUkkgfiAwICsgQ0VVICsgQ0hTLCBkYXRhID0gcGhlbm8pCiAgIyBzZXQgdXAgYXhlcwogIGF4aXNfeCA8LSBzZXEobWluKHBoZW5vJENFVSksIG1heChwaGVubyRDRVUpLCBieSA9IGdyYXBoX3Jlc28pCiAgYXhpc195IDwtIHNlcShtaW4ocGhlbm8kQ0hTKSwgbWF4KHBoZW5vJENIUyksIGJ5ID0gZ3JhcGhfcmVzbykKICAjIHNhbXBsZSBwb2ludHMKICBsbV9zdXJmYWNlIDwtIGV4cGFuZC5ncmlkKENFVSA9IGF4aXNfeCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIENIUyA9IGF4aXNfeSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIEtFRVAuT1VULkFUVFJTID0gRikKICBsbV9zdXJmYWNlJFlSSSA8LSBwcmVkaWN0KGxvZXNzX21vZGVsLCBuZXdkYXRhID0gbG1fc3VyZmFjZSkKICBsbV9zdXJmYWNlIDwtIGFjYXN0KGxtX3N1cmZhY2UsIENIUyB+IENFVSwgdmFsdWUudmFyID0gIllSSSIpCiAgIyBjcmVhdGUgcGxvdAogIHBsdCA8LSBwbG90X2x5KHBoZW5vLAogICAgICAgICAgICAgICAgIHggPSB+Q0VVLAogICAgICAgICAgICAgICAgIHkgPSB+Q0hTLAogICAgICAgICAgICAgICAgIHogPSB+WVJJLAogICAgICAgICAgICAgICAgIHR5cGUgPSAic2NhdHRlcjNkIiwKICAgICAgICAgICAgICAgICBtb2RlID0gIm1hcmtlcnMiLAogICAgICAgICAgICAgICAgIG1hcmtlciA9IGxpc3Qoc2l6ZSA9IDIpLAogICAgICAgICAgICAgICAgIHRleHQgPSBwaGVubyRJRCkKICAjIGFkZCBzdXJmYWNlCiAgcGx0IDwtIGFkZF90cmFjZShwID0gcGx0LAogICAgICAgICAgICAgICAgICAgeiA9IGxtX3N1cmZhY2UsCiAgICAgICAgICAgICAgICAgICB4ID0gYXhpc194LAogICAgICAgICAgICAgICAgICAgeSA9IGF4aXNfeSwKICAgICAgICAgICAgICAgICAgIHR5cGUgPSAic3VyZmFjZSIpCiAgCiAgcGx0ICAKfSkKYGBgCgojIyMgVHJ5IGFnYWluIHdpdGggZGlmZmVyZW50IG9yZGVyIG9mIHZhcmlhYmxlcyAtIHByZWRpY3RpbmcgWVJJIG1heWJlIG5vdCBhIGdvb2QgaWRlYSBnaXZlbiBsZXZlbCBvZiB2YXJpYXRpb24KCmBgYHtyLCB3YXJuaW5nPUZ9CmNvbG91cnNjYWxlcyA8LSBjKCJWaXJpZGlzIiwgIkhvdCIsICJFbGVjdHJpYyIpCnRpdGxlcyA8LSBjKCJFZHVjYXRpb25hbCBBdHRhaW5tZW50IiwgIkhlaWdodCIsICJTa2luL2hhaXIgcGlnbWVudGF0aW9uIikKCmNvdW50ZXIgPC0gMApsYXBwbHkoYWxfZnJlcV9kZl9zaHVmZiwgZnVuY3Rpb24ocGhlbm8pewogIGNvdW50ZXIgPDwtIGNvdW50ZXIgKyAxCiAgIyBzZXQgZ3JhcGggcmVzb2x1dGlvbgogIGdyYXBoX3Jlc28gPC0gMC4wNQogICMgZ2V0IGxtIGZvciBkYXRhCiAgbG9lc3NfbW9kZWwgPC0gbG9lc3MoQ0VVIH4gMCArIENIUyArIFlSSSwgZGF0YSA9IHBoZW5vKQogICMgc2V0IHVwIGF4ZXMKICBheGlzX3ggPC0gc2VxKG1pbihwaGVubyRDSFMpLCBtYXgocGhlbm8kQ0hTKSwgYnkgPSBncmFwaF9yZXNvKQogIGF4aXNfeSA8LSBzZXEobWluKHBoZW5vJFlSSSksIG1heChwaGVubyRZUkkpLCBieSA9IGdyYXBoX3Jlc28pCiAgIyBzYW1wbGUgcG9pbnRzCiAgbG1fc3VyZmFjZSA8LSBleHBhbmQuZ3JpZChDSFMgPSBheGlzX3gsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBZUkkgPSBheGlzX3ksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBLRUVQLk9VVC5BVFRSUyA9IEYpCiAgbG1fc3VyZmFjZSRDRVUgPC0gcHJlZGljdChsb2Vzc19tb2RlbCwgbmV3ZGF0YSA9IGxtX3N1cmZhY2UpCiAgbG1fc3VyZmFjZSA8LSBhY2FzdChsbV9zdXJmYWNlLCBZUkkgfiBDSFMsIHZhbHVlLnZhciA9ICJDRVUiKQogICMgY3JlYXRlIHBsb3QKICBwbHQgPC0gcGxvdF9seShwaGVubywKICAgICAgICAgICAgICAgICB4ID0gfkNIUywKICAgICAgICAgICAgICAgICB5ID0gfllSSSwKICAgICAgICAgICAgICAgICB6ID0gfkNFVSwKICAgICAgICAgICAgICAgICB0eXBlID0gInNjYXR0ZXIzZCIsCiAgICAgICAgICAgICAgICAgbW9kZSA9ICJtYXJrZXJzIiwKICAgICAgICAgICAgICAgICBtYXJrZXIgPSBsaXN0KHNpemUgPSAyKSwKICAgICAgICAgICAgICAgICB0ZXh0ID0gcGhlbm8kSUQpIAogIHBsdCA8LSBhZGRfdHJhY2UocGx0LAogICAgICAgICAgICAgICAgICAgeiA9IGxtX3N1cmZhY2UsCiAgICAgICAgICAgICAgeCA9IGF4aXNfeCwKICAgICAgICAgICAgICB5ID0gYXhpc195LAogICAgICAgICAgICAgIHR5cGUgPSAic3VyZmFjZSIsCiAgICAgICAgICAgICAgY29sb3JzY2FsZSA9IGNvbG91cnNjYWxlc1tjb3VudGVyXSkgJT4lIAogICAgbGF5b3V0KHRpdGxlID0gdGl0bGVzW2NvdW50ZXJdKQogIHBsdAp9KQpgYGAKCgo=